1. Encapsulation
Introduction
- A.封装表现
- 方法就是一个最基本封装体
- 类其实也是一个封装体
- B.封装的好处
注意:
- 开发中一般只使用public和private。
如果类用public修饰,则类名必须与文件名相同。一个文件中只能有一个public修饰的类。
Setter and Getter
类中不需要对外提供的内容都应该私有化,包括属性和方法。私有化所有的属性 (成员变量) ,必须写对应的get/set方法。
注意: 私有仅仅是封装的体现形式而已this
当创建对象的时候,Java为该对象创建一个this指针用来存储对象在 heap 中的地址。
当类中存在成员变量和局部变量同名的时候为了区分,就需要使用this关键字。class Person {private int age;private String name;public Person(int age, String name){this.age = age;this.name = name;}}
2. Inheritance
A. Introduction
在Java中,类的继承是指在一个现有类的基础上去构建一个新的类,构建出来的新类被称作子类,现有类被称作父类。
继承关系的子类特点
子类会自动拥有父类所有非private修饰的属性和方法
通过子类对象既可以调用自身的非private修饰的成员,也可以调用父类的非private修饰的成员。
继承的好处
继承的出现提高了代码的复用性(re-usability),提高软件开发效率
-
继承的注意事项
在Java中,类只支持单继承,不允许多继承,也就是说一个类只能有一个直接父类
- 多个类可以继承一个父类
- 多层继承是可以的
-
B. 语法
class SubClass extends BaseClass {public SubClass(<attributes>){super(<base attributes>)}....}
C. super
java中的
super关键字是一个引用变量,用于引用直接父类对象。
每当创建子类的实例时,父类的实例被隐式创建,由super关键字引用变量引用。
javasuper关键字的用法如下: super可以用来引用直接父类的实例变量。super可以用来调用直接父类方法。-
D. Override 覆盖
成员变量的调用:
子类的对象调用成员变量的时候,子类自己有,使用子类,子类自己没有调用的父类。
当子父类中出现了同名成员变量, 在子类中需要访问父类重名成员变量时,需要使用super关键字。
成员方法的调用:
子类的对象调用方法的时候,子类自己有,使用子类,子类自己没有调用的父类。
子类中出现与父类一模一样(方法的返回值类型 方法名 参数列表都要一样)的方法时,会出现覆盖操作,也称为override重写。
注意事项:
Permission:子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
- 方法定义:子类方法和要重写的父类的方法:方法的方法名和参数列表都要一样。关于方法的返回值:
- 如果是基本数据类型,子类的方法和重写的父类的方法返回值类型必须相同
- 如果是引用数据类型,子类的方法和重写的父类的方法返回值类型可以相同或者子类方法的返回值类型是父类方法返回值类型的子类
@Override 注解:only shows the compiler that you would like to override a method. If the method signature is not known in a super class or an implemented interface you get a compile time error. At runtime, there is no difference.
3. Abstract Class and Interface
A. Abstract Class 抽象类
使用abstract修饰的类是抽象类。抽象类中可以定义多个抽象方法。如果一个类继承于一个抽象类,则子类必须实现父类的所有抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为abstract类。
抽象类和普通类的主要有三点区别:
抽象类和抽象方法都需要被abstract修饰。抽象方法一定要定义在抽象类中。
- 抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。
- 抽象类不能用来创建对象;
抽象类的设计思想:
分析事物时,发现了共性内容,就出现向上抽取。会有这样一种特殊情况,就是方法功能声明相同,但方法功能主体不同。那么这时也可以抽取,但只抽取方法声明,不抽取方法主体。抽象类中是否可以不定义抽象方法?
是可以的,那这个抽象类的存在到底有什么意义呢?不让该类创建对象,方法可以直接让子类去使用(适配器设计模式)。 ```java public abstract class Animal { public void sleep(){
} }System.out.println("动物睡觉");
public class Cat extends Animal{ }
public class Test { public static void main(String[] args) { //Cat c = new Cat(); new Cat().sleep();//不让该类创建对象,方法可以直接让子类去使用 } }
<a name="A2djU"></a>### 抽象关键字abstract不可以和哪些关键字共存?`private` , `final`, `static`<a name="4QkGU"></a>## B. Interface 接口接口是一种特殊的类。如果说类的内部封装了成员变量、构造方法和成员方法,那么**接口的内部主要就是封装了方法**,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法(JDK 9)。接口将具体的实现交给它的实现类(相当于接口的子类)来完成。这样将功能的定义与实现分离,优化了程序设计。<a name="Zr9gR"></a>### Define interface```javapublic interface 接口名 {//propertiespublic static final 数据类型 变量名 = 值;//必须定义为静态常量, 可以被类名和接口名直接调用//methodspublic abstract 返回值类型 方法名1(参数列表);//公共访问的抽象方法: 可以省略public abstract,建议书写public default 返回值类型 方法名2(参数列表);//默认方法: default不可省略,供子类调用不必重写。public static 返回值类型 方法名3(参数列表);//静态方法:供接口直接调用。public private 返回值类型 方法名4(参数列表);//私有方法:供接口中的默认方法或者静态方法调用。}
Implement interface
接口最重要的体现:解决单继承的局限性。将多继承这种机制在java中通过实现多个接口完成了。
- 怎么解决单继承的局限性呢?
如果继承多个类时,当多个父类中有相同功能时,子类调用会产生不确定性。其实核心原因就是在于多继承父类中的function有方法体,而导致调用运行时,不确定运行哪个方法体的内容。
- 为啥接口能解决呢?
- 接口中,有多个抽象方法时,接口中的function都没有方法体,实现类必须重写所有抽象方法。如果抽象方法有重名的,只需要重写一次。
- 接口中,有多个默认方法时,实现类都可继承使用。如果默认方法有重名的,必须重写一次。
- 接口中,存在同名的静态方法并不会冲突,原因是只能通过各自接口名访问静态方法。
```java public interface LiveAble { public abstract void move(); public default void fly(){ System.out.println(“天上飞”); } }
public class Bird implements LiveAble { @Override public void move(){ fly(); } }
public class Dog implements LiveAble { @Override public void move(){ System.out.println(“地上跑”); } }
<a name="CrEaD"></a>### Inherit Interface多个接口之间可以使用extends进行继承```javainterface Fu1{void show();}interface Fu2{void show1();}interface Fu3{void show2();}interface Zi extends Fu1,Fu2,Fu3{void show3();}
C. Abstract Class v.s. Interface
相同点:
- 都不能直接实例化对象,必须被extends or implement;
- 都包含abstract method, 其子类都必须override这些抽象方法;
区别:
- 抽象类可以包含非抽象方法; 接口只能包含抽象方法;
- 一个类只能继承一个直接父类(可能是抽象类),却可以实现多个接口;(接口弥补了Java的单继承)
- 抽象类定义这个事物中应该具备的共性内容。接口定义这个事物中的额外功能
二者的选用:
优先选用接口,尽量少用抽象类;
需要定义子类的行为,又要为子类提供共性功能时才选用抽象类;
4. Polymorphism 多态
A. 多态的三种体现方式
多态体现为父类引用变量可以指向子类对象。
多态的前提是必须有子父类关系或者类实现接口关系,否则无法完成多态。
在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。
/*父类类型 变量名 = new 子类类型();变量名.方法名();*/Person p1 = new Student();Person p2 = new Teacher();p1.work(); //p1会调用Student类中重写的work方法p2.work(); //p2会调用Teacher类中重写的work方法//抽象类 变量名 = new 抽象类子类();public abstract class Fu {public abstract void method();}class Zi extends Fu {public void method(){System.out.println("重写父类抽象方法");}}Fu fu= new Zi();fu.method();//接口 变量名 = new 接口实现类();interface Fu {public abstract void method();}class Zi implements Fu {public void method(){System.out.println(“重写接口抽象方法”);}}Fu fu = new Zi();fu.method();
B. 多态成员方法的特点
成员变量:编译和运行都看父类。
当子父类中出现同名的成员变量时,多态调用该变量时:
编译时期:参考的是引用型变量所属的类中是否有被调用的成员变量。没有,编译失败。
运行时期:也是调用引用型变量所属的类中的成员变量。
简单记:编译和运行都看父类。
成员方法:编译看父类,运行看子类。
编译时期:参考引用变量所属的类,如果没有类中没有调用的方法,编译失败。
运行时期:参考引用变量所指的对象所属的类,并运行对象所属类中的成员方法。
简而言之:编译看父类,运行看子类。
C. Casting
多态的转型分为向上转型与向下转型两种:
- 向上转型:当有子类对象赋值给一个父类引用时,便是向上转型,多态本身就是向上转型的过程。
使用格式:Person p = new Student(); - 向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。如果是直接创建父类对象,是无法向下转型的!
使用格式:Person p = new Student(); Student stu = (Student) p; //变量p 实际上指向Student对象```java //描述动物类,并抽取共性eat方法 abstract class Animal { public abstract void eat(); }
// 描述狗类,继承动物类,重写eat方法,增加lookHome方法 class Dog extends Animal { void eat() { System.out.println(“啃骨头”); }
void lookHome() {System.out.println("看家");}
}
public class Test { public static void main(String[] args) { Animal a = new Dog(); //多态形式,创建一个狗对象 a.eat(); // 调用对象中的方法,会执行狗类中的eat方法 // a.lookHome();//使用Dog类特有的方法,需要向下转型,不能直接使用
// 为了使用狗类的lookHome方法,需要向下转型// 向下转型过程中,可能会发生类型转换的错误,即ClassCastException异常// 那么,在转之前需要做健壮性判断if( !a instanceof Dog){ // 判断当前对象是否是Dog类型System.out.println("类型不匹配,不能转换");return;}Dog d = (Dog) a; //向下转型d.lookHome();//调用狗类的lookHome方法}
