面向对象三大特征
封装
封装性在Java中的体现:
1.方法就是一种封装
2.关键字private也是一种封装
可以阻止不合理的代码进入
一旦使用了private进行修饰,那么本类当中仍然可以随意访问。
但是!超出了本类范围之外就不能再直接访问(XXX.xxx)了。
间接访问private成员变量:就是定义一对Getter/Setter方法
必须叫setXxx或者是getXxx命名规则
对于Getter来说,不能有参数,返回值类型和成员变量对应
对于Setter来说,不能有返回值,参数类型和成员变量对应
但是,对于基本类型中的boolean值,Getter方法一定写成isXxx的形式,而
public class Person{
private String name;
private int age;
private boolean male;
public void show(){
System.out.println("我叫:" + name + ",年龄:" + age);
}
//这个成员方法,专门用于向age设置数据
//只能使用void,并且必须时setAge格式
public void setAge(int num){
age = num;
}
//这个成员方法专门用于获取age数据
//无参数、有返回值、且返回值类型和成员变量类型对应
public int getAge(){
return age;
}
public void setMale(boolean b){
male = b;
}
public boolean isMale(){
return male;
}
}
封装就是将一些细节信息隐藏起来,对于外界不可见。
继承
继承是多态的前提,没有继承就没有多态
java.lang.Object类是继承类的顶端
继承主要解决的问题就是共性抽取
共性抽取:几个类里有多个共性,则重新定义一个新类,新类中定义存在的共性部分,然后其他类自底向上继承新类。
父类(基类、超类)
将公共的内容抽取到父类当中
子类(派生类)
子类中自动拥有父类中内容的特性,可以拥有自己专有的内容。
在继承关系中,“子类就是一个父类”,子类可以被当做父类看待
例如父类是员工,子类是讲师,那么“讲师是一个员工”
继承定义的格式
定义父类的格式(一个普通类的定义)
public class 父类名称{
//....
}
定义子类的格式
public class 子类名称 extends 父类名称 {
//...
}
例子:
//定义一个父类:员工
public class Employee {
public void method(){
System.out.println("....");
}
}
//定义一个员工的子类:讲师
public class Teacher extends Employee {
//虽然不写,但是已经继承了父类中的方法等!
//可以调用父类中的方法等!
}
继承中成员变量的访问特点:
父类new出来的对象只能使用父类的东西,没有任何子类的内容
在父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式
直接通过子类对象访问成员变量:new上一个子,然后.就是直接
等号左边是谁,就优先用谁,没有则向上找
Zi zi = new Zi();
System.out.println(zi.num);//num是父类和子类中重名部分
//zi是由Zi类new出来的,等号左边是Zi,优先用子类中
zi.abc//子类中没有,再查找上一级父类,如果都没有,就报错
间接通过成员方法访问成员变量
该方法属于谁(定义在哪个类),就优先用谁,没有则向上找
public class Fu {
int numFu = 10;
int num = 100;
public void methodFu(){
//使用的本类当中,不会向下找子类的
System.out.println(num);//100
}
}
区分子类方法中重名的三种变量
局部变量: 直接用
本类成员变量:this.成员变量名
父类成员变量:super.成员变量名
继承中成员方法的访问特点
在不重名的情况下,子类在创建对象后也可以调用父类的方法
但是重名的情况下,创建的对象是谁,就有限用谁,没有则向上找
无论是成员方法还是成员变量,在子类中没有的话,都是向上寻找。
重写(Override)
在继承关系中,方法名称一样,参数列表也一样。(覆盖、覆写)
特点:
创建的是子类对象,则优先用子类方法。
注意事项:
- 1、父子类之间方法名称相同,参数列表也应该相同。
- 2、在子类中覆盖方法上一行写上@Override,可以检测是否为覆盖重写
- 3、子类方法的返回值范围必须 ≤ 父类方法的返回值范围
- 4、子类方法的权限必须 ≥ 父类方法的修饰符
public > protected > (default) > private
(default)并不是关键字,而是什么都不写,留空
应用场景:体现一下设计原则
设计原则:对于已经投入使用的类,尽量不要进行修改。
推荐定义一个新的类,来重复利用其中共性内容,并且添加改动新内容
(问题:如果原来的类当中,有效率不高的方法,是不是还有可能会被调用,出现bug呢??)
方法中利用super.父类方法名可以继承这个方法,之后再添加新功能
继承中构造方法的访问特点:
1、子类构造方法当中有一个默认隐含的“super()【无参父类方法】”调用,所以一定时先调用的父类构造,后执行子类构造。
2、子类构造可以通过super关键字来调用父类重载调用
3、super的父类构造调用,必须是子类构造方法并且是第一个语句,不能多次调用super构造
4、并且有参和无参只能选一个,因为肯定会有一个变成后面的声明调用
public Zi(){
super();
}
public void method(){
super(); ×××错误
}
super关键字(主要用来访问父类内容)
1、在子类的成员方法中,访问父类的成员变量
2、子类的成员方法中,访问父类的成员方法
public void method(){
super.method(); //调用父类中的成员方法
System.out.println("555");
}
3、子类的构造方法中,访问父类的构造方法
public Zi(){
super(); //会赠送一个无参数
}
this关键字(访问本类内容)
1、在本类的成员方法中,访问本类的成员变量
2、在本类的成员方法中,访问本类的另一个成员方法
public void methodA(){
}
public void methodB(){
this.methodA();
.....
}
3、在本类的构造方法中,访问本类的另一个构造方法
并且this(...)也必须是构造方法的第一个语句,意味着一个构造方法中只能调用一次
super和this两种构造调用,不能同时使用
public Zi(){
this(10); //本类的无参构造调用本类的有参
}
public Zi(int n){
...
}
重载(Overload)
方法名称一样,参数列表不一样。
抽象类:
所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,
如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类
抽象方法:加上abstract关键字,然后去掉大括号,直接分号结束
抽象类:抽象方法所在的类,必须是抽象类才行。在class之前加上abstract
如何使用抽象类和抽象方法:
1、不能直接创建new抽象类,对象都是具体的,抽象类不能被实例化。
2、必须用一个子类来继承抽象父类
3、子类必须覆盖重写抽象父类当中所有的抽象方法
覆盖重写(实现):子类去掉抽象方法的abstract关键字,然后补上方法体大括号。
接口
因为Java只允许单继承,接口就是为了实现类似多继承的功能
接口就会一种公共的规范标准,只要符合规范标准,就可以通用
格式
public interface 接口名称 {
//接口内容
}
由class换成关键字interface之后,编译生成的字节码文件仍然是
.java --> .class
Java7,接口包括:
1.常量
2.抽象方法
Java8,额外包括:
3.默认方法
4.静态方法
Java9,额外包括:
5.私有方法
接口的抽象方法定义:
1.接口的抽象方法,修饰符必须是两个固定的关键字:public abstract
2.两个关键字可以选择性省略
3.方法的三要素可以随意定义
public interface MyInterfaceAbstract {
public abstract void myMethod();
// private abstract void mythod(); Error!!!
abstract void myMethod1();
public void myMethod2();
void myMethod3();
}
接口的抽象方法使用:
接口使用步骤:
1.接口不能直接使用,必须有一个“实现类”来“实现”接口
格式:
//区别于 public class 实现类名 extends 父类{
// }
public class 实现类名称 implements 接口名称 {
//...
}
2.接口的实现类必须覆盖重写接口中所有的抽象方法
3.创建实现类的对象,进行使用
不能直接new接口类,要new它的实现类
如果实现类并没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类,(如果有抽象方法没管的,就好比把这些抽象方法继承下来,含继承方法必须是抽象类)
而抽象类(即原本的实现类)又是不能new的。
//接口
public interface MyInterfaceAbstract {
public abstract void myMethod();
// private abstract void mythod(); Error!!!
abstract void myMethod1();
}
//未全部重写的实现类,必须定义为抽象类
public abstract class MyInterfaceAbstractImp implements MyInterfaceAbstract{
// 未重写的类
// @Override
// public void myMethod() {
// System.out.println("1");
//
// }
@Override
public void myMethod1() {
System.out.println("2");
}
}
//而抽象类使用必须定义一个子类继承
public class Test extends MyInterfaceAbstractImp{
// 接口的实现类未重写的,会被实现类继承下来,
// 若要使用,又必须由新定义的子类给覆盖重写
@Override
public void myMethod() {
}
}
接口的默认方法定义:
存在原因:
为了解决接口升级的问题(比如接口中的抽象方法有变化)
格式:
public default 返回值类型 方法名(参数列表) {
方法体
}
把原本接口中,新添加的方法改成默认方法
默认方法会被实现类给继承下去
public default void methodDefault(){
//...
}
注意事项:
1.接口的默认方法,可以通过接口实现类对象,直接调用
2.接口的默认方法,也可以被接口实现类进行覆盖重写
接口的静态方法
接口是允许定义静态方法
格式:
public static 返回值类型 方法名称(参数列表){
//方法体
}
不能通过接口实现类的对象来调用接口当中的静态方法!!!(一个类可以有多个接口,多个静态方法有可能会产生冲突)(默认方法就不冲突??)
正确用法:
接口名称.静态方法名(参数);
使用接口需要注意:
1.接口没有静态代码块,不能有构造方法
2.一个类的直接父类有且仅有一个,但是一个类可以同时实现多个接口
public class MyInterfaceImpl implements MyinterfaceA, MyintefaceB {
//覆盖重写左右抽象方法
}
3.如果实现类所实现的多个接口中,存在重复的抽象方法,那么只需要覆盖重写一次即可
4.如果实现类没有覆盖重写所有接口的所有抽象方法,那么实现类必须是一个抽象类
5.如果实现类所实现的多个接口中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写
6.一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先使用父类当中的方法
public class Zi extends Fu implements interfaceA, interfaceB{
//...
}
多态
指一个对象含有多种形态
小明即使一个学生,也是一个人类
既有学生形态,也有人类形态
多态实际指的是同一个对象,在不同时刻体现出来不同的状态。
格式:
父类名称 对象名 = new 子类名称();
为什么要用多态
节省代码量,多个子类继承于一个父类时,只需要更改子类名称就可以
多态成员变量访问特点:
同继承一样
成员变量。编译看左边,运行看左边
多态方法访问特点:
构造方法:创建子类对象时访问父类的无参构造方法,对父类数据初始化
静态方法:编译看左边,运行看左边。(静态只能被静态重写,不需要看成是方法重写,因为静态与类有关)
成员方法:编译看左边,运行看右边。(因为成员方法重写,其实重写与否,调用的都是子类的成员方法)
编译看左边,运行看右边
Fu obj = new Zi();
obj.method();//父子共有方法,编译通过,但是运行看右边,
// 因此输出 子类中的method
obj.methodFu();//编译看左边,左边为父类,编译通过
obj.methodZi();//编译看左边,但此为子类,编译不通过
父类一定要有此成员,子类可以没有,因为可以继承。
(父类不为抽象情况下)
如果父类有而子类没有此方法,调用父类方法只是表面现象,其本质是调用了子类的方法,因为子类继承了这个方法。
如果该方法体内调用了一个方法,此方法在子类中也重写了
那么此刻代码是调用了是子类方法
对象的向上转型:
就是多态写法
父类名称 对象名 = new 子类名称();
Animal obj = new Cat();
右侧创建一个子类对象,把它当做父类来看待使用
向上转型一定是安全的,从小范围转向大范围
弊端:对象一旦向上转型为父类,无法访问子类中独有的方法
解决方案:用对象的向下转型--还原
对象的向下转型:
一个【还原动作】,类似基本数据类型中的强制转换
int num = (int) 10.0; //可以
int num = (int) 10.5; //不可以
子类名称 对象名 = (子类名称) 原本父类对象;
Animal obj = new Cat(); //定义一个多态,向上转型
Cat cat = (Cat)obj; //向下转型
将父类对象,【还原】成本来的子类对象
注意事项:
- 必须保证对象本来创建的时候是啥,才能向下转型变成啥,否则报错
如何知道父类的引用是指向哪个子类
动物类引用是指向猫还是狗
格式:
对象 instanceof 类名称
得到一个boolean值,判断前面对象能不能当做后面类型是实例。
public class Dinstanceof {
public static void main(String[] args) {
//Animal类引用指向Dog类 多态,向上转型
Animal animal = new Dog();
animal.eat();
//根据if判定,如果指向不是Cat类,则返回值为0,进入不了
//结果是,进入不了
if (animal instanceof Cat){
Cat cat = (Cat) animal;
cat.methodZi(); //Cat类独有方法
}
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.methodDog(); //Dog类独有方法
}
}
//在另一个方法中使用判定
giveMeAPet(new Dog());
}
public static void giveMeAPet(Animal animal) {
if (animal instanceof Cat){
Cat cat = (Cat) animal;
cat.methodZi(); //Cat类独有方法
}
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.methodDog(); //Dog类独有方法
}
}
三种方式:
1.向上转型,转好了再放入参数列表 OK
2.不进行转型,放入参数列表 OK
3.使用子类对象、匿名对象 OK
类比 基本数据类型的自动转化
如果需要使用实现类中的特有方法:
先用instanceof进行判断后,向下转型再调用,最好用else if
下例:Usb接口、Laptop类、Mouse实现类、keyBoard实现类
public class Demo08Main {
public static void main(String[] args) {
//创建一个笔记本
Laptop computer = new Laptop();
computer.powerOn();
1.向上转型,转好了再放入参数列表 OK
//准备一个鼠标,供电脑使用
Mouse mouse = new Mouse();
//首先进行向上转型
Usb usbMouse = mouse; //or:Usb usbMouse = new Mouse();多态写法
//参数是Usb类型,正好传进去Usb鼠标
computer.useDevice(usbMouse);
2.不进行转型,放入参数列表 OK
//准备一个键盘,供电脑使用
keyBoard key = new keyBoard();
//or:Usb usbKey = new keyBoard(); 多态写法
// computer.useDevice(usbKey); //没有使用多态写法 正确!!
//方法参数是Usb类型,传递进去是方法实现类
computer.useDevice(key); //正确!!也发生了向上转型成为接口类型,小范围到了大范围,自动转化
3.使用子类对象、匿名对象 OK
computer.useDevice(new keyBoard()); //使用子类对象,匿名对象都OK
//实现类 --> 接口,我需要接口,但是实现类向上转型为接口,于是可以使用
computer.powerOff();
}
}
Laptop类,使用实现类的独有方法
public void useDevice(Usb usb) {
usb.open();//打开设备
if (usb instanceof Mouse) {
//向下转型
Mouse mouse = (Mouse) usb;
mouse.click();
} else if (usb instanceof keyBoard){
//向下转型
keyBoard key = (keyBoard) usb;
key.pang();
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。