参考教程

JDK8帮助文档

狂神说Java视频

封装

程序设计要求:高内聚,低耦合

  • 高内聚:类的内部数据操作细节自己完成,不允许外部干涉
  • 低耦合:尽量暴露少量的方法给外部使用

封装(数据的隐藏):通常应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。

REMEMBER:属性私有,get/set

关键字:private

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
public class Student {
//属性私有,利用修饰符关键字private
private String name;
private int id;
private char sex;
private int age;

//提供一些可以操作private属性的方法:
//public的get&set方法

//get:获得属性数据
public String getName(){
return this.name;
}

//set:给这个属性数据设置值
public void setName(String name){
this.name = name;
}


public int getAge() {
return age;
}

public void setAge(int age) {
//通过set方法可以规避不合法的值
if (age > 120 || age < 0){
this.age = 3;
}else {
this.age = age;
}

}
}

main方法测试:

1
2
3
4
5
6
7
8
9
10
11
12
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
//不能通过s1.name操作name属性,
//如果是public String name;就可以
s1.setName("Messi");
System.out.println(s1.getName());

s1.setAge(999);
System.out.println(s1.getAge());
}
}

IDEA生成get&set方法快捷键:Alt+insert,选择Getter&Setter,再选择需要生成get&set方法的属性名称。

封装的意义

  1. 提高程序安全性,保护数据。
  2. 隐藏代码的实现细节
  3. 统一接口
  4. 提高系统可维护性

继承

继承的本质:对某一批类的抽象,从而实现对现实世界更好的建模。

关键字:extends

  • Java中类只有单继承,没有多继承。即一个子类只能有一个父类(只能直接继承一个类),一个父类可以有多个子类。

  • 继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
  • 继承关系的两个个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。
  • 子类和父类之间,从意义上讲应该具有“is a”的关系。
1
2
3
4
5
6
7
8
9
10
11
12
13
//人:父类
public class Human {
public int money = 10_0000_0000;

public void say(){
System.out.println("说话");
}
}

//学生 is 人 派生类:子类
//子类继承了父类,就会拥有父类的全部非 private 的属性、方法。
public class Classmate extends Human {
}

main测试:

1
2
3
4
5
6
7
public class Application {
public static void main(String[] args) {
Classmate classmate = new Classmate();
classmate.say();
System.out.println(classmate.money);
}
}

若一个类有final修饰符则不能被继承,final属于断子绝孙修饰符。

Object类

在Java中所有的类都默认直接或间接继承Object类

IDEA打开继承树快捷键:Ctrl+H

super

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
//人:父类
public class Human {
protected String name = "Person";

public void say(){
System.out.println("Hello");
}

public Human(){
System.out.println("父类Human构造无参执行了");
}
}

//球员 继承 Human类
public class Player extends Human {
private String name = "Player";

public void test1(String name){
System.out.println(name);//输出传入的实参
System.out.println(this.name);//输出本类的属性name
System.out.println(super.name);//输出父类的属性name
}

public void say(){
System.out.println("Shoot");
}

public void test2(){
say();//调用本类里的say方法
this.say();//调用本类里的say方法
super.say();//调用父类里的say方法
}

public Player(){
//首先调用父类的无参构造,有一句隐藏代码:super();
//该调用必须放在子类构造器的第一行,否则报错
// Call to 'super()' must be first statement in constructor body
//如果父类采用了有参构造后没有写无参构造,
// 子类构造器里必须用带参的super方法显式调用父类的有参构造器
System.out.println("子类Player构造无参执行了");
}
}

main测试:

1
2
3
4
5
6
7
8
9
public class Application {
public static void main(String[] args) {
//在new的过程中,首先调用父类构造器,再调用子类构造器
Player player = new Player();

player.test1("Messi");
player.test2();
}
}

super注意点:

  1. super调用父类的构造方法,必须在构造方法的第一个
  2. super必须只能出现在子类的方法或者构造方法中
  3. superthis不能同时调用构造方法

superVSthis

  • 代表的对象不同:
    • this:本身调用者的这个对象
    • super:代表父类对象的引用
  • 前提不同:
    • this:没有继承也可以使用
    • super:只能在继承条件下才可以使用
  • 构造方法不同:
    • this():本类的构造器
    • super():父类的构造器

方法重写

重写都是方法,和属性无关

  • 静态方法:
1
2
3
4
5
6
7
8
9
10
11
public class B {
public static void test(){
System.out.println("B->test()");
}
}

public class A extends B {
public static void test(){
System.out.println("A->test()");
}
}

main测试:

1
2
3
4
5
6
7
8
9
10
11
public class Application {
public static void main(String[] args) {
//方法的调用只和左边定义的数据类型有关
A a = new A();
a.test();//A->test()

//父类的引用指向了子类对象
B b = new A();
b.test();//B->test()
}
}
  • 非静态方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class B {
public void test(){//IDEA左侧有向下箭头提示“Is overridden in A”
System.out.println("B->test()");
}
}

public class A extends B {
//Override 重写
@Override //注解:有功能的注释
public void test() {//IDEA左侧有向上箭头提示“Overrides method in B”
//默认调用父类方法:super.test();
System.out.println("A->test()");
}
}

IDEA生成get&set方法快捷键:Alt+insert,选择Override Method,或者直接Ctrl+O。

main测试:

1
2
3
4
5
6
7
8
9
10
public class Application {
public static void main(String[] args) {
A a = new A();
a.test();//A->test()

//父类的引用指向了子类对象
B b = new A();//子类重写了父类的方法
b.test();//A->test()
}
}

静态与非静态方法的区别:

  • 静态方法是类的方法,非静态方法是对象的方法。

  • 静态方法:方法的调用只和左边定义的数据类型有关,静态方法可以被继承,但是不能被覆盖,即不能重写。

  • 非静态方法:可以重写,名字、参数一模一样,且方法修饰符必须是public

只有普通的方法调用可以是多态的

重写总结:

  • 前提:需要有继承关系,是面向一个方法来说的,子类重写父类的方法。
  • 方法名必须相同
  • 参数列表必须相同(不同则为重载
  • 修饰符:范围可以扩大但不能缩小,public>protected>default>private
  • 抛出的异常:范围可以被缩小但不能扩大,例如ClassNotFoundException<Exception
  • 子类的方法和父类必须一致,方法体不同

重写的意义:

  • 父类的功能子类不一定需要或不一定满足

多态

多态:即同一方法可以根据发送对象的不同而采用多种不同的行为方式

核心思想:一个对象的实际类型是确定的,但可以指向对象的引用类型有很多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class People {
public void run(){
System.out.println("People RUN");
}
}

public class Worker extends People {
@Override
public void run() {
System.out.println("Worker RUN");
}

public void eat(){
System.out.println("Worker EAT");
}
}

main测试:

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
public class Application {
public static void main(String[] args) {
//一个对象的实际类型是确定的
//new People();
//new Worker();

//指向的引用类型不确定【需要有继承关系】

//Worker能调用的方法都是自己的或者是继承于父类的
Worker w1 = new Worker();

//People可以指向子类,但是不能调用子类独有的方法
People w2 = new Worker();//父类的引用指向子类的类型

Object w3 = new Worker();
//引用类型 实际类型

w1.run();
w2.run();
//在子类重写之前均输出People RUN,
// 重写之后均输出Worker RUN

//直接写w2.eat();报错
//证明:能执行哪些方法主要看左边的引用类型,
// 与右边实际类型关系不大
((Worker) w2).eat();//高转低,需要强制转换类型,然后就可以调用子类独有的方法

}
}

多态注意事项:

  • 多态是方法的多态,属性没有多态
  • 需要有父类和子类的继承关系,没有的话会报类型转换异常(ClassCastException)
  • 存在条件:继承关系,方法被需要重写,父类引用指向子类对象:Father f1 = new Son()
  • 无法重写的方法:static(属于类不属于实例)、final(常量)、private

instanceof

二元操作符,测试它左边的对象是否是它右边的类的实例,返回boolean的数据类型。

1
2
3
4
5
6
7
8
public class People {
}

public class Worker extends People {
}

public class Teacher extends People {
}

main测试:

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
public class Application {
public static void main(String[] args) {
//Object->People->Worker
//Object->People->Teacher
//Object->String
Object o = new Worker();

System.out.println(o instanceof Object);//true
System.out.println(o instanceof People);//true
System.out.println(o instanceof Worker);//true
System.out.println(o instanceof Teacher);//false
System.out.println(o instanceof String);//false

People p = new Worker();
System.out.println(p instanceof Object);//true
System.out.println(p instanceof People);//true
System.out.println(p instanceof Worker);//true
System.out.println(p instanceof Teacher);//false
//System.out.println(p instanceof String);报错:Inconvertible types; cannot cast 'OOP.Polymorphism.People' to 'java.lang.String'

Worker w = new Worker();
System.out.println(w instanceof Object);//true
System.out.println(w instanceof People);//true
System.out.println(w instanceof Worker);//true
//System.out.println(w instanceof Teacher);报错
//System.out.println(w instanceof String);报错
}

}

System.out.println(X instanceof Y);能否编译通过看左边的引用类型X与Y有没有继承关系,实际判断结果看右边的实际类型里X是不是Y的实例(子类或本类)。

OOP:编译看左,运行看右。

类型转换

父类:高,子类:低。从高到低需要强制转换。

1
2
3
4
5
6
7
8
9
10
11
public class People {
public void run(){
System.out.println("People RUN");
}
}

public class Worker extends People {
public void go(){
System.out.println("Worker GO");
}
}

main测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Application {
public static void main(String[] args) {
//高:父类 低:子类
People obj = new Worker();
//obj.go();报错:Cannot resolve method 'go' in 'People'

//将这个obj强制转换为Worker类型就可以使用Worker里的独有方法
//从People转到Worker:从高到低,需要强制转换
((Worker) obj).go();

Worker w = new Worker();
w.go();
//从低转高:不用强制转换
People p = w;
//p.go();报错:子类转换为父类有可能丢失自己本来独有的方法

}
}

类型转换总结:

  • 存在条件:父类引用指向子类的对象
  • 子类转换为父类:向上转型,不用强制转换,丢失子类中可直接调用的独有的方法
  • 父类转换为子类:向下转型,需要强制转换,丢失父类被子类重写掉的方法。
  • 意义:方便方法的调用,减少重复的代码,使代码更简洁。