跟随狂神学Java
作者:joker2yue
链接:https://github.com/Joker2Yue/Joker2Yue-Blog
来源:Github
著作权归原作者所有。商业转载请联系原作者获得授权,非商业转载请注明出处。
第十天:面向对象-3
人类最大的敌人是傲慢,其次是无知
学习内容
封装
该露的露,该藏的藏
封装(数据的隐藏)
- 通常,应禁止直接访问一个对象中数据的实际表现,而应通过操作接口来访问,这称为信息隐藏
记住这句话就够了
==属性私有,get/set==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.joker_yue.javalearn.OOP;
public class Student {
private String name;
private int id;
private char sex;
}
|
封装的好处
- 提高程序的安全性
- 隐藏代码的实现细节
- 统一接口
- 提高了系统的维护性
继承
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
“extends”的意思是”扩展“。子类是父类的继承
Java中只有单继承,没有多继承
继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等
继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。
子类和父类之间,从意义上讲应该具有”is a“的关系
object类
super
方法重写
1 2 3 4 5 6 7 8 9 10 11 12
|
package com.joker_yue.javalearn.OOP.demo05;
public class Person {
public void say(){ System.out.println("说了一句话"); } }
|
1 2 3 4 5 6 7 8
|
package com.joker_yue.javalearn.OOP.demo05;
public class Student extends Person{ }
|
1 2 3 4 5 6 7 8 9 10 11 12
|
package com.joker_yue.javalearn.OOP.demo05;
public class Application { public static void main(String[] args) {
Student s1 = new Student(); s1.say(); } }
|
- ==Object类==:在Java中,所有的类默认直接或者间接继承Object类(爷爷辈的)
1 2 3 4 5 6 7 8 9
|
package com.joker_yue.javalearn.OOP.demo05;
public class Person {
protected String name = "Joker"; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
package com.joker_yue.javalearn.OOP.demo05;
public class Student extends Person{ protected String name = "Yue";
public void test(String name){ System.out.println(name); System.out.println(this.name); System.out.println(super.name);
} }
|
上述代码解释了super”指针”的用法
1 2 3 4 5 6 7 8 9 10 11
|
package com.joker_yue.javalearn.OOP.demo05;
public class Application { public static void main(String[] args) {
Student s1 = new Student(); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13
|
package com.joker_yue.javalearn.OOP.demo05;
public class Person { public Person(){ System.out.println("Person无参执行"); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12
|
package com.joker_yue.javalearn.OOP.demo05;
public class Student extends Person{ public Student(){ System.out.println("Student无参执行"); } }
|
上述代码执行后,会生成以下信息:
我们发现是先调用了父类的无参构造,才调用了子类的无参构造。所以我们断定,在位置1
绝对有一句隐藏代码super();
也就是:
1 2 3 4
| public Student(){ super(); System.out.println("Student无参执行"); }
|
但我们不能移动super();
语句的位置,它必须放在第一句。
但我们的类定义extends==继承自其他类==时,它将隐式的先加载父类。
super注意点:
- super调用父类的构造方法,必须在构造方法的第一个
- super必须只能出现在子类的方法或者构造方法中
- super和this不能同时调用构造方法
VS this
- 代表的对象不同
this:本身调用者这个对象
super:代表父类对象的引用
前提
this:没有继承也可以使用
super:只能在继承条件下使用
构造方法
this():本类的构造
super():父类的构造
方法重写
==不同于方法重载!==
引言:在上节课我们了解到:如果子类继承自父类,且父类中有与子类相同名字、参数的方法时,对象调用方法时将会优先调用父类中的。我们如果想让他调用子类中的方法,就需要用到方法重写
1 2 3 4 5 6 7 8 9
|
package com.joker_yue.javalearn.OOP.demo05;
public class A extends B{ public static void test() { System.out.println("A=>test()"); } }
|
1 2 3 4 5 6 7 8 9 10
|
package com.joker_yue.javalearn.OOP.demo05;
public class B { public static void test() { System.out.println("B=>test()"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
package com.joker_yue.javalearn.OOP.demo05;
public class Application { public static void main(String[] args) {
A a = new A(); a.test();
B b = new A(); b.test(); }
}
|
上述中A是未重载方法的代码,继承自B。此时运行Application.java,会输出以下信息:
1 2 3 4 5 6 7 8 9 10 11
|
package com.joker_yue.javalearn.OOP.demo05;
public class A extends B{ @Override public void test() { System.out.println("A=>test()"); } }
|
1 2 3 4 5 6 7 8 9 10
|
package com.joker_yue.javalearn.OOP.demo05;
public class B { public void test() { System.out.println("B=>test()"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
package com.joker_yue.javalearn.OOP.demo05;
public class Application { public static void main(String[] args) { A a = new A(); a.test();
B b = new A(); b.test(); }
}
|
运行Application.java,会输出以下信息:
所以静态方法和非静态的方法区别很大
即b是A new出来的对象,因此调用了A的方法
==因为静态方法是类的方法,而非静态是对象的方法==
有static时,b调用了B类的方法,因为b是用B类定义的
没有static时,b调用的是对象的方法,而b是用A类new的
总结:static修饰的方法归类所有,叫类的成员,不叫对象的成员(详细可搜static关键字)。
注意:子类要重写父类方法 ,父类方法不一定必须要public 。只要子类重写方法的权限修饰符不必父类更严格就行
使用:因为子类会完全继承父类的方法,但有些时候,子类需要与父类不同的方法,就要进行方法重写
- 需要有继承关系,子类重写父类的方法
- 方法名必须相同
- 参数列表必须相同
- 修饰符:子类重写方法的权限修饰符不必父类更严格就行(父private,子public)
- 抛出的异常:范围可以被缩小,但不能被扩大(ClassNotFoundException < Exception)
多态
引言:我们new了一个子类的对象,子类里面有继承父类的方法。
在方法重写的前提下,方法名参数都相同,系统该调用哪个的呢?
答案是看前面类的类型,写的父类就调父类,写的子类就调子类
在我们创建对象的时候,创建的对象类型是已知的,但是,对象可以指向的引用类型就不确定了
1 2 3 4 5
| Student s1 = new Student(); Person s2 = new Student(); Object s3 = new Student();
|
接下来我们看看类
1 2 3 4 5 6 7 8 9
|
package com.joker_yue.javalearn.OOP.demo06;
public class Person { public void run(){ System.out.println("run"); } }
|
1 2 3 4 5 6 7 8 9 10
|
package com.joker_yue.javalearn.OOP.demo06;
public class Student extends Person{ @Override public void run() { System.out.println("son"); } }
|
我们尝试在Application.java中运行下示代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
package com.joker_yue.javalearn.OOP.demo06;
public class Application { public static void main(String[] args) { Student s1 = new Student(); Person s2 = new Student(); Object s3 = new Student();
s2.run(); s1.run(); } }
|
它最终会输出
原因:在s2.run();
中,子类Student重写了父类的方法,执行时优先执行子类的方法
现在我们在Student类中加个eat方法:
1 2 3 4 5 6 7 8 9 10 11 12 13
|
package com.joker_yue.javalearn.OOP.demo06;
public class Student extends Person{ @Override public void run() { System.out.println("son"); } public void eat(){ System.out.println("eat"); } }
|
并尝试在Application.java输出:
会发现报错,是因为s2中没有eat()方法。(s2的引用类型是Person类)
所以,对象能使用的方法,由引用类型决定。(由左边决定,和右边关系不大)
1 2
| Person s2 = new Student(); 引用类型 对象名 = 创建对象类型
|
于是
1 2 3 4
| Student s1 = new Student();
Person s2 = new Student();
|
多态的注意事项:
多态是方法的多态,属性没有多态
父类和子类。否则会类型转换异常ClassCastException
存在条件
- 继承关系
- 方法需要重写
存在条件:继承关系,方法需要重写,父类引用指向子类对象 Father f1 = new Son();
有些方法无法重写,比如static方法,因为它是属于类的,和类一起加载的,不属于实例
还有final常量
private方法
多态的概念,可以理解为:方法的调用除了本类对象可以调用自己以外,在方法重写里还可以通过父类对象的引用来调用自己
instanceof关键字
可以判断两个类之间是否有父子关系
比如我们有下列代码
1 2 3 4 5 6 7
|
package com.joker_yue.javalearn.OOP.demo06;
public class Person { }
|
1 2 3 4 5 6 7
|
package com.joker_yue.javalearn.OOP.demo06;
public class Student extends Person{
}
|
1 2 3 4 5 6 7
|
package com.joker_yue.javalearn.OOP.demo06;
public class Teacher extends Person {
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
package com.joker_yue.javalearn.OOP.demo06;
public class Application { public static void main(String[] args) { Object obj = new Student(); System.out.println(obj instanceof Student); System.out.println(obj instanceof Person); System.out.println(obj instanceof Object); System.out.println(obj instanceof Teacher); System.out.println(obj instanceof String); } }
|
运行Application.java,会生成下列消息:
1 2 3 4 5
| true true true false false
|
这里3个true,2个false
3个true比较好理解,因为obj是Student类的对象。输出语句中都是Student类的父类
2个false是因为不是父类
然后我们尝试new一个新对象,让其引用指向Person类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.joker_yue.javalearn.OOP.demo06;
import java.io.ObjectStreamField;
public class Application { public static void main(String[] args) { Person obj2 = new Student(); System.out.println(obj2 instanceof Student); System.out.println(obj2 instanceof Person); System.out.println(obj2 instanceof Object); System.out.println(obj2 instanceof Teacher); System.out.println(obj2 instanceof String); } }
|
其中【1】位置语句不等编译就提示出错了。原因是Obj2的==引用==Person类继承自Object类,而语句中的String类也是。使用同父类的两个子类不能相互比较
我们将【1】语句删除后再运行,会输出以下信息:
那我们再试试new一个新对象,其引用指向Student类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.joker_yue.javalearn.OOP.demo06;
import java.io.ObjectStreamField;
public class Application { public static void main(String[] args) { Student obj3 = new Student(); System.out.println(obj3 instanceof Student); System.out.println(obj3 instanceof Person); System.out.println(obj3 instanceof Object); System.out.println(obj3 instanceof Teacher); System.out.println(obj3 instanceof String); } }
|
也是可以看到【1】【2】直接报错了,还是一样的原因,obj3的引用与Teacher拥有相同的父类。
我们同样将其删除再运行
总结:编译看左边,运行看右边。
强制转换
引言:上节课我们学习了多态,多态可以按照我们的想法定义对象引用的类,从而调用引用的类中的方法。但是,我们不一定总是只使用引用的类的方法,可能我们会用回来原来对象的类的方法。这时候我们需要强制转化。
接下来我们向Person类和Student类中写点方法
1 2 3 4 5 6 7 8 9 10
|
package com.joker_yue.javalearn.OOP.demo06;
public class Person { public void run(){ System.out.println("run"); } }
|
1 2 3 4 5 6 7 8 9 10
|
package com.joker_yue.javalearn.OOP.demo06;
public class Student extends Person{ public void go(){ System.out.println("go"); } }
|
1 2 3 4 5 6 7 8 9 10 11
|
package com.joker_yue.javalearn.OOP.demo06;
public class Application { public static void main(String[] args) { Person obj = new Student(); obj.go(); } }
|
此时【1】语句报错,原因是obj的引用类型为Person类,Person类中没有go方法。
如果我们想要能让obj使用go()方法,我们需要将其转化为Student类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
package com.joker_yue.javalearn.OOP.demo06;
import java.io.ObjectStreamField;
public class Application { public static void main(String[] args) {
Person obj = new Student();
Student tempObj = (Student) obj; tempObj.go(); } }
|
虽然我们定义其引用为高类型,但是我们可以通过类型转化将其转化为低类型,这样就可以使用低一级的方法了
【1】【2】也可以直接写成这样:
总结与思考:在各种编程语言中,从低转高的转化总是隐式地发生,比如int可以自动转化为double类型。在Java中,子类也可隐式的转化为父类,但是可能会丢失部分子类的方法。
低(子)转高(父)时,由于子已经继承了父的所有,所以删去属于自己的后自然而然就可以转化问父类的;而父想要转子,则需要重新开辟只属于子的空间,则需用强制转换。
不同的是,从高转低一般不会隐式的发生,因为这可能发生精度或方法的丢失。如果实在需要,则需要开发者显式定义出来。用在本节,就是强制转化