第五章 继承

1. JAVA中所有继承都是公有继承。

子类会继承父类的每一个域和方法(存疑),但是父类中域的访问控制修饰符会决定子类中可不可以直接访问父类的域。例如,子类中不能直接访问父类的私有域,只能通过公有的get方法访问。同样方法的访问权限也是由父类中访问控制修饰符决定的。
关于继承会不会继承私有的域和方法,http://www.cnblogs.com/aademeng/articles/6191691.html

2. super与this

this关键字有两个用途:

  1. 引用隐式参数,this为指向本对象的引用。
  2. 调用该类的其他构造器。
    super关键字有两个用途:
  3. 调用超类的方法以及访问超类的域(访问权限由超类访问控制修饰符决定)。
  4. 调用超类的构造器。

注意调用构造器语句只能作为另一构造器的第一条语句。
this是一个引用,可以将其赋值给另一对象变量。而super只是关键字,不能将其赋值给对象变量。

3. 上转型对象。

将子类对象的引用赋值给父类对象时,这个父类对象称为上转型对象。

  1. 上转型对象不能访问的子类对象新增的域和方法。
  2. 上转型对象可以操作子类继承或隐藏的域,也可以使用子类继承的或重写的方法。
  3. 如果子类中有与父类中同名的域,那么访问的是父类中未被重写的域。
  4. 如果子类中有重写的方法,那么访问的是子类中重写的方法,并且该方法访问可以子类中新增的域和方法。
    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    public class Main {

    public static void main(String[] args) throws InterruptedException {

    Person person = new Student("super",21,"1407084125","sub");
    System.out.println(person.name);
    System.out.println(person.getName());
    person.printInfo();

    Thread.sleep(1000*60);
    }
    }

    //Person.java
    public class Person {

    public String name;

    private int age;

    public Person(String name, int age) {
    this.name = name;
    this.age = age;
    }

    public int getAge() {
    return age;
    }

    public void setAge(int age) {
    this.age = age;
    }

    public String getName(){
    return name;
    }

    public void printInfo(){
    System.out.println("Person{" +
    "name='" + name + '\'' +
    ", age=" + age +
    '}');
    }

    }

    //Student.java

    public class Student extends Person {

    private String studentNum;

    public String name;

    public Student(String superName, int age, String studentNum, String subName) {
    super(subName, age);
    this.studentNum = studentNum;
    this.name = subName;
    }

    public String getStudentNum() {
    return studentNum;
    }

    public void setStudentNum(String studentNum) {
    this.studentNum = studentNum;
    }

    @Override
    public String getName() {
    return name;
    }

    @Override
    public void printInfo(){
    System.out.println("Student{" +
    "studentNum='" + getStudentNum() + '\'' +
    ", name='" + name + '\'' +
    '}');
    }
    }
    4. 方法调用过程
  1. 编译器查看对象的声明类型和方法名,假设调用x.f(param),且x声明为C类的对象。编译器将会 一 一列举所有C类中名为f的方法和其超类中可访问且名为f的方法。需要注意的是:有可能存在多个名为f,但参数类型不一样的方法。至此,编译器已获得所有可能被调用的候选方法。
  2. 编译器将查看调用方法时的参数类型。在之前获取到的候选方法列表中存在与调用方法参数完全匹配,那么就调用这个方法。这个过程被称为重载解析(overloading resolution)。 至此,编译器获取到了需要调用的方法名称和参数类型。(方法名称和参数类型被称为方法签名。)
  3. 如果是private方法static方法final方法 或者构造器,那么编译器将可以准确地知道应该调用哪个方法,我们将这种调用方式称为静态绑定 (static binding)。于此对应的是,调用的方法依赖于隐式参数的实际类型,并且在运行时实现动态绑定(dynamic binding)。
  4. 当程序运行,并且采用动态绑定调用方法时,虚拟机一定调用与x所引用对象的实际类型最合适的那个类的方法。假设x的实际类型是D,它是C类的子类。如果D类定义了方法f(String),就直接调用它;否则,将在D类的超类中寻找f(String),以此类推。

其实这个地方不是理解的很透彻,等看完《深入理解JAVA虚拟机》再琢磨。还有动态绑定和静态绑定。先留一片文章图解JVM执行引擎之方法调用 - 陈洋Cy - 博客园

5. final类的方法自动成为final方法,但不包括域。final类和final方法的主要目的是不在子类中改变语义。
6. instanceof

instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。

7. 抽象类
  1. 包含一个或者多个抽象方法的类必须声明为抽象类,但是还可以包含具体的数据和方法。
  2. 即使不含有抽象方法,也可以声明为抽象类。
  3. 抽象类不能被实例化,但是可以定义一个抽象类的对象变量,但是他只能引用非抽象子类的对象。
  4. 子类可以部分实现抽象方法或者不实现,只要仍然含有抽象方法就必须声明为抽象的。
8. ArrayList类

ArrayList是一个泛型类。

  1. add() 添加元素.
  2. remove() 删除元素
  3. set() 设置元素,注意不能超过size()的大小。
  4. get() 访问元素
9. 对象包装器
  1. 有时候需要将基本类型转换为对象。所有的基本类型都有与之对应的包装类。这些类均为不可变类
  2. 编译器能够自动装箱和自动拆箱。编译器会在编译时自动插入必要的方法调用。
10. 可变参数方法

main()方法中的Sring[] args参数便是一个可变参数的例子,同样可以将main方法定义为
public static void main(String ... args) {}
效果是一样的。

1
2
3
4
5
6
7
8
9
10
public static int max(int ... nums){
int mmax = Integer.MIN_VALUE;
for (int i:nums){
mmax = Integer.max(mmax,i);
}
return mmax;
}
public static void main(String ... args) {
System.out.println(max(1,2,3,4));
}
11. 枚举类
  1. 最基本的枚举类:
    1
    2
    3
    4
    5
    public enum Size {
    SMALL,
    MEDIA,
    LARGE
    }
    枚举默认有两个域,name和ordinal,name即为上面定义中的SMALLMEDIALARGE,而orinal为枚举常量的 位置(下标),从零开始计数。
  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
    public enum Sex {
    MALE("男"),
    FEMALE("女");

    private String value;

    Sex(String value) {
    this.value = value;
    }

    public String getValue() {
    return value;
    }

    public void setValue(String value) {
    this.value = value;
    }


    @Override
    public String toString() {
    return "Sex{" +
    "value='" + value + '\'' +
    '}';
    }
    }
  3. 默认toString()返回name值,可以重载,例如上面的代码。
  4. valueOf(Class enumClass , String name)返回指定类型指定名字的枚举类。
  5. 每个枚举类型都有一个静态的values()方法,返回包含所有枚举值的数组。
11. 异常

http://blog.csdn.net/hguisu/article/details/6155636

异常有两种类型:已检查异常(checked exception,又称编译时异常)和未检查异常(unchecked exception,又称运行时异常)。

  1. 已检查异常
    除了运行时异常以外的所有异常都被称作已检查异常,编译器会在编译期间去检查程序是否处理了它们。如果这些异常没有 被处理,将会出现编译错误。
    已检查异常的例子:
  • ClassNotFoundException
  • IllegalAccessException
  • NoSuchFieldException
  • EOFException
  1. 未检查异常
    运行时异常也被称作为未检查异常。编译器并不会检查是否有程序员处理这些异常,但程序员有处理这些异常的责任,并程序提供一个安全的退出。
    未检查异常的例子:
  • ArithmeticException
  • ArrayIndexOutOfBoundsException
  • NullPointerException
  • NegativeArraySizeException
  1. 异常的层次结构

    检查异常:除了RuntimeException及其子类以外,其他的Exception类及其子类都属于检查异常。
    未检查异常 :包括运行时异常(RuntimeException与其子类)和错误(Error)。
  2. 异常的处理方式
  • try-catch捕获异常
  • throws,throw抛出异常
12. Class类
  1. Object类的getClass()方法可以返回一个Class类的实例
  2. Class类getName() 方法返回类的名字,如果在一个包里,则包名作为类名的一部分
  3. Class类的静态方法forName(String className) 获得类名对应的Class对象。
  4. 一个Class类对象表示的是一个类型,而这个类型未必一定是一种类。如int不是类,但int.class 是一个Class类的对象。
  5. Class类的newInstance()可以创建一个该类的实例化对象,调用默认构造函数,如果没有默认构造函数,抛出异常。
13. 利用反射检查类的结构。

在java.lang.reflect包中有三个类Field、Method和Constructor分别描述类的域、方法和构造器。这三个类都有一个叫getName的方法,用来返回项目的名称。Field类有一个getType方法,用来返回描述域所属的Class对象。Method和Constructor类有能够报告参数类型的方法,Method还有一个可以报告返回类型的方法。这三个类有一个getModifiers的方法,它将返回一个整数值,用不同的位开关描述public和static这样的修饰符的使用状况,还可以利用Modifier.toString方法将修饰符打印出来。

Class类的getFields、getMethods和getCostructors方法可以获得类提供的public域(Returns an array containing Field objects reflecting all the accessible public fields of the class or interface represented by this Class object.)、方法(including those declared by the class or interface and those inherited from superclasses and superinterfaces)和构造器数组(Returns an array containing Constructor objects reflecting all the public constructors of the class represented by this Class object.),其中包括从超类继承的公有成员。Class类的getDeclatedFields、getDeclatedMethods和getDeclaredConstructors方法可以获得类中声明的全部域、方法和构造器,其中包括私有和受保护的成员,但不包括超类的成员。

14. 运行时利用反射分析类。

利用反射机制可以查看在编译时还不清楚的对象域。
查看对象域的关键方法是Field类中的get方法。如果f是一个Field类型的对象,obj是某个包含f域的类的对象,f.get(obj)将返回一个对象,其值为obj与的当前值。
既然能够得到域的值,那么也就能设置域的值。可以使用Field类中的set方法设置域的值,用法为f.set(obj,value),obj是包含f域的对象,value是要设置的值。
还要注意,如果f是一个私有域,那么直接使用get方法会抛出一个IllegalAccessException异常。除非拥有访问权限,否则Java安全机制只允许查看任意对象有哪些域而不允许得去域的值。
反射机制的默认行为受限于Java的访问控制。然而,如果一个Java程序没有受到安全管理器的控制,就可以覆盖访问控制。可以调用Field、Method或Constructor类中的setAccessible方法达到这个目的:
f.setAccessible(true);
setAccessible方法是AccessibleObject类中的一个方法,它是Field、Method和Constructor类的超类。

15. 反射泛型数组

暂时不看。

16. 调用任意方法

在C和C++中,可以从函数指针执行任意函数。虽然Java没有提供函数指针,但反射机制允许调用任意方法。
和Field中的get方法类似,Method类中有个invoke方法,它允许调用包装在当前Method对象中的方法。invoke方法的签名是:
Object invoke(Object obj,Object... args);
如果是静态方法,第一个参数会被忽略,可以将其设置为null。