0%

Java8 OCA Review

Java Building Blocks

Understanding the Java Class Structue

Basic Class Structure

在Java中,类是最基本的建造块。要使用类就要思考使用对象(类在运行中的基本实例)。各种类的对象就代表了程序的当前状态。

Fields and Methods

Java类最基本的两个要素就是域(属性或者变量)和方法(函数或者过程)。域和方法叫做类的成员。
变量就是内存中的对象,代表了程序的状态。方法是改变变量的状态,从而改变程序的状态。

1
2
3
4
5
6
7
8
9
10
11
12
public class Animal {
private String name; // field

// method
public String getName() {
return this.name;
}

public String setName(String name){
this.name = name;
}
}

Main Method

一个Java的程序必须开始于main方法,main方法是gateway是Java进程的入口。通过main进入JVM的世界,从而分配资源,读各个文件。

1
2
3
4
5
6
7
8
9
10
11
// Zoo.java
public class Zoo {
public static void main(String[] args) {

}
}

// 编译
$ javac Zoo.java
// 运行
$ java Zoo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Zoo.java
public class Zoo {
public static void main(String[] args) {
System.out.println(args[0]); // Bronx
System.out.println(args[1]); // Zoo
}
}

// 编译
$ javac Zoo.java
// 运行
/* The output is what you might expect:
Bronx
Zoo
*/
$ java Zoo Bronx Zoo

Understanding Package Declarations and Imports

Java类会有很多,需要分类管理,就是用于管理类,那么需要packageimport来实现这个管理特色。

Methods and Encapsulation

除了main方法,还有一般的方法需要了解。方法有很多修饰符,但是最基本是方法签名(方法名称和参数列表)和返回值(没有返回值,就要使用这个void)。首先一个方法要考虑它的访问性,是完全公开public,还是完全private,还是可以让包内和子类调用,或者只是包内(默认)。

public The method can be called from any class.

private The method can only be called from within the same class.

protected The method can only be called from classes in the same package or subclasses. You’ll learn about subclasses in Chapter 5.

Default (Package Private) Access The method can only be called from classes in the same package. This one is tricky because there is no keyword for default access. You simply omit the access modifier.

然后考虑是否其他可选修饰符,比如static、abstract、final、synchronized、native、strictfp

之后就是返回值、方法名和参数列表

参数列表可以添加可变长变量

Class Design

Hard Remember

Java类最基本的两个要素

Java类最基本的两个要素就是域(属性或者变量)和方法(函数或者过程)。域和方法叫做类的成员。域也叫做实例变量。

Comments

几种形式:single line & multiple lines

1
2
3
4
5
6
7
8
9
10
11
12
13
//

/*
*
*
*/


/**
*
*
* @author
*/
1
2
3
4
5
6
7
8
9
注意的是编译器是这样去解释问题的!

对于单行注释, 只要//之后的同一行上面都会被注释掉

对于多行注释,编译器视“/* */”为一个块,遇到/*就认为注释块开始了,当再次遇到*/就会认为注释模块结束。

所以以下这种情况就是会导致最后面的*/部分编译器报错。

/* */ */
![](/images/posts/java-oca-review/comment-note.png)

Classes vs. Files

  • 类文件是*.java

  • Java的文件名必须和public修饰的类名相同,必须大写。

  • 一个文件中可以有多个类,但是只能有一个public的类

主方法 Main长什么样子的和Terminal参数入口

一个Java的程序必须开始于main方法,main方法是gateway是Java进程的入口。通过main进入JVM的世界,从而分配资源,读各个文件。

1
2
3
4
5
6
7
8
9
10
11
12
// 编译运行的指令
// Zoo.java
public class Zoo {
public static void main(String[] args) {

}
}

// 编译
$ javac Zoo.java
// 运行
$ java Zoo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Zoo.java
public class Zoo {
// 参数就是从运行文件之后开始算起
public static void main(String[] args) {
System.out.println(args[0]); // Bronx
System.out.println(args[1]); // Zoo
}
}

// 编译
$ javac Zoo.java
// 运行
/* The output is what you might expect:
Bronx
Zoo
*/
$ java Zoo Bronx Zoo

package和import

  • 包名是有层次感的

    从Toplevel到lowlevel, com.amazon.java8.my.name

  • 通配符(wildcards)

    • 只通配类名,import后面只能有一个*

      1
      2
      3
      4
      5
      import java.nio.*; // NO GOOD – a wildcard only matches //class names, not "file.*Files"

      import java.nio.*.*; // NO GOOD – you can only have one wildcard //and it must be at the end

      import java.nio.files.Paths.*; // NO GOOD – you cannot import methods //only class names

      使用这个因为你所需要的类都在这个包当中

      1
      2
      3
      4
      5
      6
      7
      import java.util.*; // imports java.util.Random among other things 
      public class ImportExample {
      public static void main(String[] args) {
      Random r = new Random();
      System.out.println(r.nextInt(10));
      }
      }
    • 尽管通配全部,但是编译器并不会将全部导入,而是根据需要而导入

  • 冗余的import

    • 冗余两种情况

      • java.lang是自动导入的包,你自己手动添加就会多余了,也不会报错。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        // line 1 2 4 冗余,4的是因为3已经导入了
        1: import java.lang.System;
        2: import java.lang.*;
        3: import java.util.Random;
        4: import java.util.*;
        5: public class ImportExample {
        6: public static void main(String[] args) {
        7: Random r = new Random();
        8: System.out.println(r.nextInt(10));
        9: }
        10:}
      • 另外一种冗余是因为类都在同一个包中,那么自然就会冗余了,因为Java会自动寻找同一个包中的其它类

  • 命名冲突

    • case 1

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      // Java gives you the compiler error: The type Date is ambiguous
      import java.util.Date
      import java.sql.Date
      public class Conflicts {
      Date date; // some more code
      }

      solution:
      remove the java.sql.Date import that we don’t need
      change to this way:
      import java.util.Date;
      import java.sql.*;
      ### a class name takes precedence over any wildcards present ###
      ### 类的名字优先于通配符
- case 2

    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/*
If You Really Need to Use Two Classes with the Same Name…

Sometimes you really do want to use Date from two different packages. When this happens, you can
pick one to use in the import and use the other’s fully qualified class name (the package name, a
dot, and the class name) to specify that it’s special. For example:
*/

import java.util.Date;

public class Conflicts {

Date date;
java.sql.Date sqlDate;

}

/*
Or you could have neither with an import and always use the fully qualified class name:
*/

public class Conflicts {

java.util.Date date;
java.sql.Date sqlDate;

}

创建对象

  • 构造器constructor
    • 类名和构造器名相同
    • 没有返回值
    • 用于初始化实例变量,当然你喜欢用来干什么就是干什么
    • 初始化对象一定需要用
  • 代码块(code block)
  • 实例初始化块(Instance Initializer Blocks)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // How many blocks do you see in this example? How many instance 
    // initializers do you see?
    // There are three code blocks and one instance initializer.
    // 怎么数? 代码块,看多少对{}, 实例初始化块就看方法外只有一对{}

    3: public static void main(String[] args) {
    4: { System.out.println("Feathers"); }
    5: }
    6:
    7: {
    8: System.out.println("Snowy");
    9: }
  • 初始化顺序

    • 实例变量(field)和实例初始化块是按它在文件中的位置
    • 构造器是在实例变量和实例初始化块结束后运行
  • 基本类型(primitive types)

    • boolean, true or false, true
    • byte, 8-bit integral value, 123
    • short, 16-bit integral value, 123
    • int, 32-bit integral value, 123
    • long, 64-bit integral value, 123
    • float, 32-bit floating-point value, 123.45f
    • double, 64-bit floating-point value, 123.456
    • char, 16-bit Unicode value, ‘a’
    • a byte can hold a value from –128 to 127
    • float and double 是带有小数点的值values
    • float类型的字面量需要f在最后
    • byte, short, int, long 没有小数点
    • 整形数字字面量是以int为大小,所以当一个数字超过int能表示的范围

      1
      2
      3
      4
      long max = 3123456789; // DOES NOT COMPILE
      long max = 3123456789L; // now Java knows it is a long
      // Alternatively, you could add a lowercase l to the number.
      // But please use the uppercase L. The lowercase l looks like the number 1.
- 进制表示
    - 八进制: octal (digits 0–7), which uses the **number 0** as a prefix—for example, 017
    - 十六进制: hexadecimal (digits 0–9 and letters A–F), which uses the **number 0** followed by **x or X** as a prefix—for example, 0xFF
    - 二进制: binary (digits 0–1), which uses **the number 0** followed by **b or B** as a prefix—for example, 0b10

- 只有数字之间可以加上下划线

    
1
2
3
4
double notAtStart = _1000.00; // DOES NOT COMPILE 
double notAtEnd = 1000.00_; // DOES NOT COMPILE
double notByDecimal = 1000_.00; // DOES NOT COMPILE
double annoyingButLegal = 1_00_0.0_0; // this one compiles
  • 引用类型
    就记住他是一个对象,一个引用名字指向一块内存空间

    1
    reference -> object

    记住图形

    1
    An object in memory can be accessed only via a reference.
  • 基本数据类型和引用类型之间区别

    • 引用类型可以是null
    • 引用类型可以有方法
    • 引用类型的名字一般有大写字母,基本类型就是小写字母
  • 声明变量

    1
    2
    3
    4
    // 不同类型不能使用逗号分隔
    int num, String value; // DOES NOT COMPILE
    // 只要是重新定义一个类型名字就不能使用逗号,要用分号
    double d1, double d2; // DOES NOT COMPILE
  • 初始化变量

    1
    2
    // 只有一个初始化变量了就是i3
    int i1, i2, i3 = 0;
  • 变量名称(identifer,标识符

    • 用于类、方法和变量
    • 只能以$、_或者字母开头
    • 数字只能从第二个字符开始
    • 不能使用Java保留字
  • 默认初始化

    • 本地变量:必须在使用前初始化,光靠声明是不会被初始化的,必须确保它在块中是被初始化了
    • 实例变量(field):声明就初始化,属于具体一个对象
    • 类变变量(有static关键字):声明就初始化,属于类,被对象分享
      默认初始化变量:

      |Variable type | Default initialization value
      |————————|———————————
      |byte, short, int, long | 0
      |boolean | false
      |float, double | 0.0
      |char | \u0000(NULL) |All objects references | null

  • 变量的作用域

    • 本地变量,从声明到块结束
    • 实体变量,从声明到对象垃圾回收
    • 类变量,从声明到项目结束
  • 摧毁对象

    • 所有java的对象都在堆里面
    • System.gc()被调用,但是不保证垃圾一定回收
    • 垃圾回收发生的条件至少是对象不再被引用,对象不再被引用不代表就会马上会被垃圾回收
    • 垃圾回收器尝试回收对象,finalize可能被调用

Method

Element Value in nap() example Required?
Access modifier public no
Optional specifier final no
Return type void yes
Method name nap yes
Parameter list (int minutes) yes(can be empty)
Optional exception list throws InterruptedException no
Method body { // take a nap } yes(can be empty braces)

请按照图中的顺序记忆,顺序是重要的。

可变长变量

There are several things to be remembered in this topic.

  • array = varargs?
    No. Although varargs are transformed to the form of array, they are not the same.
  • where to put varargs in a method?
    It must be the last element in a method’s parameter list, which means you can be allowed to have only one varargs parameter in the method signature.
  • what happens when you pass in values through varargs?
    • Java will create an array of length zero for you…
    • if you pass in an array, Java will use your array
    • if you pass in a list of elements from a array, Java will help you create an array
    • if you pass in nothing, Java will create an array of length zero for you
1
2
3
4
5
6
7
8
9
10
public statc void walk(int start, int... nums){
System.out.println(nums.length);
}

public static void main(String args[]){
walk(1); // 0
walk(1, 2); // 1
walk(1, 2, 3); // 2
walk(1, new int[]{4, 5}) // 2
}

静态问题

Where to use key word static?

  • method
  • field
1
2
3
4
5
6
7
8
9
10
11
12
public class Koala{
public static int count = 0; // static variable
public static void main(String[] args){ // static method
System.out.println(count);
}
}

public class KoalaTester{
public static void main(String[] args){
Koala.main(new String[0]);
}
}

Why we need it?

  • for utility or helper methods that don’t require any object state
  • for state that is shared by all instances of a class, like a counter

How to call a static variable or method?

1
2
3
4
Koala k = new Koala();
System.out.println(k.count);
k = null;
System.out.println(k.count);
  • by classname, like Koala.count
  • by instance, like k.count, although k is null, k.count still works

Static Methods (vs. Instance)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Static {
private String name = "Static class";

public static void first(){

}

public static void second(){

}

public void third(){
System.out.println(name);
}

public static void main(String[] args){
first();
second();
third(); // cannot compile
}
}

third() is a instance method, so it cannot be inferred to static. There are two ways to modify,

  • add static to third() method, but name should changed to static as well

  • call in this way new Static().third()

Remember two rules,

  • an instance method can call a static method
  • a static method cannot call an instance method

Easy to understand, assume a non-static variable is inside a static method, when static method and variables are loaded into static area at the begining of the program, the non-static stuffs cannot be initialized at that time because they are determined by the runtime.

Static Variables

  • Some static variales are meant to change as the program runs

    1
    2
    3
    public class Counter {
    private static int counter = 0;
    }
  • Some static variales are meant to never change over time

    1
    2
    3
    4
    private static final int NUM_BUCKETS = 45;
    public static void main(String[] args){
    NUM_BUCKETS = 5; // DOES NOT COMPILE
    }
1
2
3
4
5
6
7
8
private static final ArrayList<String> values = new ArrayList<>();
public static void main(String[] args){
values.add("changed"); // it works
}
/**
this list 'values' can add more elements
as long as its reference is not changed
*/

Static Initialization(静态块)

1
2
3
4
5
6
private static final int NUM_SECONDS_PER_HOUR;
static{
int numSecondsPerMinute = 60;
int numMinutesPerHOur = 60;
NUM_SECONDS_PER_HOUR = numSecondsPerMinute * numMinutesPerHOur;
}

It is just like unnamed methods.

There is an special example we should notice.

1
2
3
4
5
6
7
8
9
10
private static int one;
private static final int two;
private static final int three = 3;
private static final int four; // cannot compile
static {
one = 1;
two = 2;
three = 3; // cannot compile
two = 4; // connot compile
}

The static variables with final must be initialized in the static initialization or at the declaration moment. Once initialized, they cannot be changed.

Static import

Please refer to javase import
Also, static imports are for importing static members of classes.

import java.util.List;
import static java.util.Arrays.asList;
public class StaticImports {
    public static void main(String[] args){
        List<String> list = asList("one", "two"); // no Arrays
    }
}

传递参数

here

构造函数

here

  • 每个构造函数的第一条语句一定时调用另外一个该类中的其它构造函数this(),或者调用父类的构造函数super()
  • super()第一条语句之后就不会被使用
  • 如果super没有被定义在构造器第一行,编译器会给你自动添加一个
  • 如果父类没有无参的构造器(意思时既没有自定义的;编译器也没有给他一个默认的,因为父类自定义了一个有残构造器),子类也没有构造器,此时编译器报错和尝试添加一个默认的无参构造函数进去子类
  • 如果父类没有一个无参构造函数,那么编译器就要求在子类的构造函数中调用父类的有惨构造函数
  • 什么时候一个类没有无参构造函数?该类只定义了有参的构造函数

调用被继承类的成员

使用super

重写方法条件

  • 子类的函数签名必须和父类的相同
  • 子类方法的可访问程度至少和父类的一样或者更加广
  • 子类方法不能扔出新的或者比父类的异常更加广的异常
  • 子类方法的返回值必须是covariant return, 即必须是父类返回值的子类或者一样

隐藏变量

当你定义了一个变量名称和父类的变量名称一样,这时候就是隐藏变量,意味着这个变量会有两份,父类一份,子类一份。