类、包和接口
本文章一些等价的名词
  • 类 / class;
  • 实例 / 对象:类的一个具体化;
  • 方法 / 函数:在Java种等价;
  • 实例变量 / 成员变量 / 实例属性 / 成员属性 / 域;
  • static变量 / 类变量 / 静态变量;
  • override 覆盖;
  • overload 重载;
  • 父类 / 超类 / superclass。

类、成员变量、方法 / 函数

  • 类是组成Java程序的基本要素。它封装了一类对象的状态和方法,是这一类对象的原型;
  • 一个类的例子:ch04.Person
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
//ch04.Person
package ch04;

public class Person extends Object{
/*
实例变量又叫域变量,成员变量访问修饰符可以是private/protected/public或者干脆没有,但是没有friend;
实例变量不能重名,例如有一个变量是name,不能再有另外一个name;
实例变量如果没有赋值,会有默认值。字符串为null,整数为0...
*/
private String name;
private int age;

public String getName() {
return name;
}

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

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
/*
构造函数同样也可以有修饰符。构造函数必须和类名字同名,且不能有类型,也不能有返回值;
构造函数可以有多个。
*/
public Person(String n, int a) {
name = n;
age = a;
}
/*
实例方法又叫成员方法、对象方法。同样可以有修饰符;
方法和构造函数最大的区别在于必须要有返回类型(如果什么都不返回则返回void)。
*/
public void sayHello() {
//局部变量
String content=null;
content = "Hello! My name is " + name + "!";
System.out.println(content);
}
public void eat() {
System.out.println("eating!");
}
public static void main(String[] args) {
//类的使用
//Person是一个类,person是类的一个实例
Person person = new Person("tom", 20);
//注意调用方式
person.sayHello();
//下面这种方法是错误的
//Person.syHellow();
//也可以这样使用
person = new Person("abc", 30);
Person p;
p = new Person("li", 19);
//和C++不同,使用完以后,不需要释放内存
//不同于C++,对象new完后不需要释放,系统会自动释放
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}

}

部分笔记在上述代码中!

构造函数

  • 必须和类同名且定义在类中;
  • 不能有任何返回值,void也不行。
1
2
3
4
5
6
7
8
public class Dog{
public Dog(){...};//Correct
public Dog(String name){...};//Correct
...
public void Dog(){...};//Wrong void
public createDog(){...};//Wrong name
}
public Dog(){...};//Wrong outside

类的封装

应该尽量只能通过对象变量来访问这个对象的变量或方法,不通过引用变量就无法访问其中的变量或方法。对于访问者而言,这个对象是封装成一个整体的,这正体现了面向对象的程序设计的“封装性”。

重载

  • 多个方法可以享有相同的名字;
  • 这些方法的参数必须不同,或者是参数个数不同,或者是参数类型不同;
  • 不能仅仅返回值类型不同。
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
//ch04.Dog
package ch04;

public class Dog {

private String name;
private int weight;

public Dog(String name, int weight) {
this.name = name;
this.weight = weight;
}
//构造函数
//构造函数的重载
public Dog( int weight,String name) {
this(name,weight);
//this.name = name;
//this.weight = weight;
//this.name表示对象的属性,name表示函数送入的参数。
}
//构造函数的重载
public Dog(int weight) {
this("noname",weight);
}
//重载的例子
//两个speak一个有参数,一个没有参数
public void speak() {
System.out.println("wang wang!");
}
//重载的例子
//两个speak一个有参数,一个没有参数
public void speak(String content) {
System.out.println(content);
}
public static void main(String[] args) {
Dog dog=new Dog( 0 );
//错误,this不能用在static方法中
//用上语句会调用构造函数,其中包含
//this.name="noname";
}
}

如下方法每两个构成一对重载

1
2
3
4
5
6
7
8
9
public void f1();
public void f1(String p1);
//
public void f1(String f1,int f2);
public void f1(int f2,String p1);
//
public void f1(String f1);
private void f1(int f1);
//修饰符可以不相同

如下方法每两个不构成一对重载

1
2
3
4
5
6
7
8
9
10
11
12
public void f1();
public int f1();
//
public void f1(String name,String address);
public void f1(String address,String name);
//
public void f1(String f1);
public void f2(String f1);
//
public void f1(String f1);
private void f1(String f1);
//不能仅用不同的访问控制来重载

this的用途1

  • 使用this解决局部变量与实例同名的问题;
  • this的上述用途1不能用在static方法中。

this的用途2

  • 在一个构造函数里面调用另外一个构造函数;
  • 可尽量减少代码的重复,便于修改。

类的继承

类的继承格式

1
2
3
4
class 父类 {
}
class 子类 extends 父类 {
}
  • 继承(inheritance)是面向对象的程序设计中最为重要的特征之一;
  • 由继承而得到的类为子类(subclass),被继承的类为父类或超类(superclass),父类包括所有直接或间接被继承的类;
  • 一个类只能有一个直接父类;
  • 子类继承父类的状态和行为,同时也可以修改父类的状态或重载父类的行为,并添加新的状态和行为;
  • 采用继承的机制来组织、设计系统中的类,可以提高程序的抽象程度,使之更接近与人类的思维方式,同时也通过继承能较好地实现代码重用,可以提高程序开发效率,降低维护的工作量。

继承

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
//ch04.Student
package ch04;

public class Student extends Person {

private String school;

@Override
public void sayHello() {
//如何在子类中调用父类地方法
super.sayHello();
System.out.println("My school is " + school);
}

public void study() {
System.out.println("I am studying! ");
}

public Student(String name, int age, String school) {
//如何在子类中调用父类的构造函数
super(name, age);
this.school = school;
}

public static void main(String[] arggs) {
Person p = new Person("Liming", 50);
p.sayHello();
Student student = new Student("Wangqiang", 20, "PKU");
student.sayHello();
student.eat();
String str="3";
str=str+student;
System.out.println(str);
}

@Override
public String toString() {
return "Student [school=" + school + ", toString()=" + super.toString() + "]";
}

}

Java中的继承

Java中的继承是通过extends关键字来实现的;

1
class SubClass extends SuperClass{...}

如果没有extends语句,则该类默认为java.lang.Objext的子类;

Java中所有的类都是通过直接或间接地继承java.lang.Object得到的。

方法地继承、覆盖与增加

  1. 方法地继承

    父类地非私有方法也可以被子类自动继承。如Student自动继承Person的方法eat();

  2. 方法的覆盖(Overriding)

    子类也可以重新定义与父类同名同参数的方法,实现对父类方法的覆盖,如sayHello();

  3. 实例方法和变量的增加

    子类可以增加新方法,如study()。

包(Package)

  • Java提供包来管理类名空间。包实际上提供了一种命名机制和可见性限制机制;
  • 包是一种松散的类的集合,一般不要求处于同一个包中的类有明确的相互关系,如包含、继承等。但是由于同一包中的类在默认情况下可以相互访问,所以为了方便编程和管理,通常把需要在一起工作的类放在一个包里。

Package语句

  • package pkg1[.pkg2[.pkg3...]];
    <!--code8-->
    
    
  • 其中,package1[.package2[.package3…]]表明包的层次,与package语句相同,它对应于文件目录,classname则指明要引入的类,如果要从一个包中引入多个类,则可以用星号(*)来代替。例如:

    1
    2
    import java.awt.*;
    import java.util.Date;
  • Java编译器为所有程序自动引入包java.lang,因此不必用import语句引入它包含的所有的类,但是若需要使用其他包中的类,必须用import语句引入;

  • 注意:使用星号(*)只能表示本层次的所有类,不包括子层次下的类;

  • 例如,经常需要用两条import语句来引入两个层次的类:

    1
    2
    import java.awt.*;
    import java.awt.event.*;

Java中的访问控制符

同一个类中 同一个包中 不同包中的子类 不同包中的非子类
private Yes
默认 Yes Yes
protected Yes Yes Yes
public Yes Yes Yes Yes

类的访问控制符

类、方法、变量前面可以加访问控制符,例如:

1
2
3
4
5
public class man()...
private String name;
protected String address;
String email;//不写访问修饰符也是一种修饰符
public static void main(String[],args)...

setter与getter

在Java编程中,有一种常见的做法,是将所有的或部分的域用private修饰,从而更好地将信息进行封装和隐藏。用setXXXX和getXXXX方法对类的属性进行存取,分别成为setter与getter。这种方法有以下优点:

  1. 属性用private更好的封装和隐藏,外部类不能随意存取和修改;
  2. 提供方法来存取对象的属性,在方法中可以对给定的参数的合法性进行检验;
  3. 方法可以完成其他必要的工作(如清理资源、设定状态等等);
  4. 只提供getXXXX方法,而不提供setXXXX方法,可以保证属性是只读的。

setter&getter

Eclipse中可自动生成setter&getter

Eclipse Auto getter&setter

修饰符static、final和abstract

static

  • static变量,又叫静态变量/类变量,归属于整个类,而不属于某个类的实例;
  • static方法:又叫静态方法/类方法,归属于整个类,而不属于某个类的实例;
  • 访问static变量/方法可以通过类名.变量名/方法名来访问;
  • 访问实例变量/方法要通过实例名.变量/方法名来访问;
  • static方法中,只能访问static变量/方法,不能访问实例变量/方法;
  • 实例方法中,既能访问static变量/方法,又能访问实例变量/方法。
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
//ch04.Count
package ch04;

public class Count {
public int myCount;
public static int totalCount;
public int getMyCount() {
return myCount;
}
public void setMyCount(int myCount) {
this.myCount = myCount;
}
public static int getTotalCount() {
return totalCount;
}
public static void setTotalCount(int totalCount) {
Count.totalCount = totalCount;
}
public static void main(String args[]) {
Count count = new Count();
count.setMyCount(1);
Count.setTotalCount(1);
System.out.println("count.myCount=" + count.getMyCount());
System.out.println("count.count=" + Count.getTotalCount());
Count count2 = new Count();
count2.setMyCount(2);
Count.setTotalCount(2);
System.out.println("count1.myCount=" + count.getMyCount());
System.out.println("count2.myCount=" + count2.getMyCount());
System.out.println("count1.count=" + Count.getTotalCount());
}
}

final

  • final类:如果一个类被final修饰符所修饰和限定,说明这个类不能被继承,即不可能有子类;
  • final方法:final修饰符所修饰的方法,是不能被子类所覆盖的方法;
  • final变量:可以近似理解成不能被修改值的变量;
  • 一个实例变量被static final两个修饰符所限定时,它实际的含义就是常量,如Integer.MAX_VALUE(表示最大整数)、Math.PI(表示圆周率)就是这种常量。在程序中,通常用static与final一起使用来指定一个常量;
  • 在定义static final时,要么在定义的时候赋值,要么在类初始化的时候static{…}赋值;
  • 在定义final实例变量时,要么在定义变量时赋初始值,要么在构造函数中赋值。
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
//ch04.TestFinal
package ch04;
public final class TestFinal {
private static final int totalNumber=0;
private static final int totalId;
static {
totalId=0;
System.out.println("class init!");
}
private final int myNumber=0;
private final int myId;
public TestFinal(){
System.out.println("instance init!");
myId=1;
}
public TestFinal(int id){
System.out.println("instance init!");
myId=id;
}
public void setMyId() {
}
public static void main(String[] args) {
new TestFinal();
}
}

abstract

  • 凡是用abstract修饰符修饰的类都被称为抽象类;

  • 抽象类不能被实例化

    Example

  • 被abstract所修饰的方法叫抽象方法,抽象方法的作用在为所有子类定义一个统一的接口。对抽象方法只需声明,而不需实现,即用分号(;)而不是用{},格式如下:

    1
    abstract returnType abstractMethod([paramlist]);
  • 抽象类中可以包含抽象方法,也可以不包含abstract方法。但是,一旦某个类中包含了abstract方法,则这个类必须声明为abstract类;

  • 抽象方法在子类中必须被实现,否则子类仍然是abstract的;

  • abstract不能和private static final native同时修饰;

  • abstract方法只能存在于abstract类中;

  • abstract类中可以没有abstract方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//ch04.AbstractTest
package ch04;
abstract class C{
abstract void callme( );
void metoo( ){
System.out.println("Inside C's metoo( ) method");
}
}
class D extends C{
void callme( ){
System.out.println("Inside D's callme( ) method");
}
}
abstract class E extends C{
}
public class AbstractTest{
public static void main( String args[ ] ){

C c = new D( );
c.callme( );
c.metoo( );
}
}

对抽象类的总结

  • 抽象类中的抽象方法没有方法体,因此无法直接实例化对象;
  • 抽象类必须有子类,子类使用extends继承抽象类,一个子类只能够继承一个抽象类;
  • 能够实现对象的子类,必须实现抽象类中全部的抽象方法。也就是说,只有当所有抽象方法不在抽象了,才能依据类,实现对象。

接口(interface)

  • 有些时候,对象之间存在明显的单一方向继承关系,例如:

Example

Example

  • Q:Java只支持单一继承

    A:

  • Java通过接口使得处于不同层次,甚至互不相关的类可以具有相同的行为;

  • 接口就是方法定义和常量值的集合。它的用处主要体现在下面几个方面:

    1. 通过接口可以实现不相关类的相同行为,而不需要考虑这些类之间的层次关系;
    2. 通过接口可以指明多个类需要实现的方法;
    3. 通过接口可以了解对象的交互页面,而不需了解对象所应对的类。
  • 在接口中定义这些共同的行为,然后由每个类分别实现这些行为;

  • 与C++不同,Java不支持多继承,而是用接口实现比多继承更强的功能;

  • 多重继承指一个类可以为多个类的子类,它使得类的层次关系不清楚,而且当多个父类同时拥有相同的成员变量和方法时,子类的行为是不确定的,这给编程带来了困难;

  • 单一继承则清楚地表明了类的层次关系,指明子类和父类各自的行为;

  • 接口则把方法的定义和类的层次区分开来,通过它可以在运行时动态地定位所调用的方法。同时接口中可以实现“多重继承”,且一个类可以实现多个接口。正式这些机制使得接口提供了比多重继承更简单、更灵活而且更强劲的功能。

接口的成员特点

  • 成员变量:只能是常量;

    默认修饰符:public static final;

  • 构造方法:没有构造方法;

  • 成员方法:只能是抽象的;

    默认修饰符:public abstract;

接口的声明语法格式

1
2
3
4
[可见度] interface 接口名称 [extends 其他的接口名]{
//声明变量
//抽象方法
}
  • 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字;
  • 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字;
  • 接口中的方法都是共有的。
1
2
3
4
5
6
7
8
9
10
11
//Example
import java.lang.*;
public interface NameOfInterface
{
//任何类型 final,static字段
//抽象方法
}
interface Animal{
public void eat();
public void travel();
}
  • 当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类;

  • 类使用implements关键字实现接口。在类声明中,implements关键字放在class声明后面;

  • 实现一个接口的语法,可以使用这个公式:

    1
    ...implements 接口名称 [,其他接口名称,其他接口名称...,...]...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//Example
public class MammalInt implements Animal{
public void eat(){
System.out.println("Mammal eats");
}
public void travel(){
System.out.println("Mammal travels");
}
public static void main(String[] args){
MammalInt m=new MammalInt();
m.eat();
m.travel();
}
}
/*
编译运行结果:
Mammal eats
Mammal travels
*/

接口的继承

使用extends关键字,子接口继承父接口的方法。

Example

接口的多继承

  • 在Java中,类的多继承是不合法的,但接口允许多继承;

  • 在接口的多继承中extends关键字只需要使用一次,在其后跟着继承接口

    1
    public interface Hockey extends Sports,Event

接口的理解

Java语言中,接口占据着非常重要的地位

  • 接口定义了Java语言当中的“全抽象”概念;
  • 接口为Java赋予了较强的扩展性,而借助于接口的扩展性,Java语言能够在不同时期找到自己的位置,从而实现持续发展;
  • 接口为Java的模块化奠定了基础。

接口和类

抽象类和接口的协同工作

  • 狗都具有eat()、sleep()方法,我们分别通过抽象类和接口定义这个抽象的概念

Example

  • 如果需要让狗拥有一项特殊的技能——钻火圈DrillFireCircle(),抽象类?or接口?
  • 一个Special Dog即可继承Dog类,又可实现DrillFireCircle()接口。

abstract&interface

  • 继承:是不是;
  • 接口:有没有;
  • 如果一个类继承了某个抽象类,则子类必定是抽象类的种类;
  • 而接口实现的则是有没有、具备不具备的关系;
  • 接口定义是为了在不同的模块/组件之间协议或契约;
  • 接口不关心内部的状态。

接口实例

1
2
3
4
5
6
7
8
//ch05.inter.swimmer
package ch04.inter;
public interface Swimmer {
public void swim();
default int getSwimSpeed () {
return 0;
}
}
  • 接口中只能包含public、static、final和没有修饰符类型的成员变量和public、abstract和没有修饰符类型的成员方法;
  • 接口中JDK8之前,接口不能定义任何实现,这意味着之前所有的Java版本中,接口指定的方法是抽象的,不包含方法体。从JDK8开始,添加了一种新功能——默认方法。默认方法允许接口方法定义默认实现,而所有子类都将拥有该方法及实现。

接口的实现及用途

  • 在类的声明中用implements语句来表示一个类使用某个接口,在类体中可以使用接口中定义的常量,而且必须实现接口中定义的所有方法。一个类可以实现多个接口。
  • 好的编程风格应该面向抽象编程而不是面向具体编程。

实现例子

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
//fish
package ch04.inter;

public class Fish extends Animal implements Swimmer {

public Fish() {
// TODO Auto-generated constructor stub
}

@Override
public void swim() {
// TODO Auto-generated method stub
System.out.println("Fish swim!");
}

@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("Fish eat!");
}

}
//frog
package ch04.inter;

public class Frog extends Animal implements Runner,Swimmer {

public Frog() {
// TODO Auto-generated constructor stub
}

@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("Frog eat!");
}

@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("Frog run!");
}

@Override
public void swim() {
// TODO Auto-generated method stub
System.out.println("Frog swim!");
}

}
//car
package ch04.inter;

public class Car implements Runner {

public Car() {
// TODO Auto-generated constructor stub
}

@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("car run!");
}

}

用途

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
package ch04.inter;

public class InterfaceUsage {

public InterfaceUsage() {
// TODO Auto-generated constructor stub
}

public static void main(String[] args) {
InterfaceUsage demo = new InterfaceUsage();
Car car = new Car();
Fish fish = new Fish();
Frog fog = new Frog();
demo.startRun(car);
demo.startRun(fog);
demo.startSwim(fish);
demo.startSwim(fog);
}

public void startRun(Runner runner) {
System.out.println("start run...");
runner.run();
System.out.println("end run...");
System.out.println("------------------------------");
}

public void startSwim(Swimmer swimmer) {
System.out.println("start swim...");
swimmer.swim();
System.out.println("end swim...");
System.out.println("------------------------------");
}
}

接口、父类和子类类型转换

  • 子类可以当作父类来用;

  • 父类不能当作子类用;

  • 实现了某个接口的子类可以当这个接口用;

  • 接口不能当子类用。

  • 强制类型转换

    (类名) 对象

    例如:String str=(String)obj。

  • 实例如下(Important!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//ch04.inter.ParentSubInterfaceAssign
package ch04.inter;
public class ParentSubInterfaceAssign {
public static void main(String[] args) {
String str="1";
Object obj=str;
str =obj;
str =(String)obj;
obj=new Object();
str =(String)obj;
Frog frog=new Frog();
Swimmer swimmer=frog;
Runner runner=frog;
frog=swimmer;
frog=(Frog)swimmer;
swimmer=new Fish();
frog=(Frog)swimmer;
}
}

抽象类和接口的区别

  • 接口的设计目的,是要求不同的类具有相同的行为。它只约束了行为的有无,但不对如何实现行为进行限制;
  • 抽象类的设计目的,是代码复用。当不同的类具有某些相同的行为,且其中一部分行为的实现方法一致时,可以让这些类都派生于一个抽象类,避免让所有的子类来重复,实现代码复用的目的。其余个性化部分,留给各个子类自己实现。

instanceof

用于判断一个对象是否是某个类/接口(或子类,或是否实现了这个接口)

用法:

1
对象名 instanceof 类名

instanceof

instanceof例子:

Example

枚举类型

ENUM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//ch04.en.EnumDirection2
package ch04.en;
static class EnumDriection2{
public enum EnumDirection2 {
EAST("东"), SOUTH("南"), WEST("西"), NORTH("北");
private EnumDirection2(String desc) {//私有类型构造函数
this.desc = desc;
}
private String desc;//存放其他属性,其他属性可以随意增减
public String getDesc() {//取得其他属性
return desc;
}
}
public static void main(String[] args) {
for(int i=0;i<EnumDirection2.values().length;i++) {
EnumDriection2 dir=EnumDirection2.values()[i];
//ordinal()取得序号;getDesc()取得自定义描述
System.out.println(dir+"="+dir.getDesc()+"\tindex="+dir.ordinal());
}
}
}

关于构造函数

再谈构造函数——关于默认构造函数

  • 必须和类同名且定义在类种;
  • 不能有任何返回值,void也不行;
  • 不能用static修饰,但是可以用public/private/protected修饰。

构造函数

一个类可以没有显式的构造函数,可以调用默认构造方法(Important!)

  • 默认构造方法是没有参数的构造方法,你可以显式定义类的默认构造方法;

  • 为了保证每个类至少有一个构造方法,如果定义的类中一个构造方法也没有写,Java将自动提供一个默认构造方法。该构造方法没有参数,用public修饰,而且方法体为空。格式如下:

    1
    public ClassName(){}
  • 只要类中显式定义了一个或多个构造方法,而且所有显式定义的构造方法都带参数,那么将失去默认构造方法。

默认构造函数

默认构造函数

构造函数里面调用构造函数用this(…)

this构造函数

  • 只能在构造函数第一行调用this(…),且只能调用一次。

构造函数中的this/super

  • 构造函数中调用自己的构造函数 this(…);
  • 调用父类的构造函数 super(…);
  • 每个构造函数在开始以前,会默认的调用其父类的无参数构造方法,除非这个类开始显式的调用了其他的父类构造函数;
  • 但是,每个类的构造函数不会默认的调用自己的无参数构造方法。

构造函数的注意事项

  • 推论1:如果一个父类,显式的声明了构造函数,且没有无参数构造函数,则其子类构造函数的第一句必须显式的调用父类的一个构造函数;
  • 推论2:如果一个父类,显式的声明了构造函数,且没有无参数构造函数,则其子类必须显式的声明构造函数。

子类调用父类构造方法

  • 在构造子类对象时,JVM会先调用父类的构造方法或者子类构造方法中通过super语句调用父类构造方法;
  • 如果子类构造方法中没有通过super语句调用父类构造方法,那么JVM会调用父类的默认构造方法,如果不存在默认构造方法,将导致编译错误。

文章源码

文章源码 备用仓库