一、面向对象
17.类的多态
多态就是同一个接口,使用不同的实例而执行不同操作
多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
父类引用指向子类对象:
Parent p = new Child();
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
Vehicle.java
public abstract class Vehicle {
public abstract void start();
}
Car.java
public class Car extends Vehicle {
@Override
public void start() {
System.out.println("一键启动,踩油门~~~");
}
}
Wife.java
public class Wife {
public void open(Vehicle v) {
v.start();
}
}
Test.java
public class Test {
public static void main(String[] args) {
Wife w = new Wife();
//父类引用v中存的是子类对象在堆中地址
Vehicle v = new Car();
w.open(v);
}
}
多态存在的三个必要条件:
- 继承
- 重写
- 父类引用指向子类对象:Parent p = new Child();
对象转型
- 向上转型、自动转型:子类类型 转 父类类型
注意:
- 向上转型就是多态
- 可以调用父类的非私有化属性、方法
- 不可以调用子类独有的属性和方法
- 可以调用子类重写的父类的方法
A.java
public class A {
String aAttr = "调用父类的非私有化属性";
public void aMethod() {
System.out.println("调用父类的非私有化方法");
}
public void method() {
System.out.println("父类的方法");
}
}
B.java
public class B extends A {
String bAttr = "子类独有的属性";
public void bMethod() {
System.out.println("子类独有的方法");
}
@Override
public void method() {
System.out.println("调用子类重写的父类的方法");
}
}
Test.java
public class Test {
public static void main(String[] args) {
A a = new B();
//调用父类的非私有化属性
System.out.println(a.aAttr);
//调用子类重写的父类的方法
a.method();
//调用父类的非私有化方法
a.aMethod();
//不可以调用子类独有的属性和方法
//a.bMethod();
//System.out.println(a.bAttr);
}
}
- 向下转型、强制转型:父类类型 转 子类类型
注意:
-
向下转型有风险,转不好会出现ClassCastException类型转型异常
错误示范:Dog dog = (Dog) new Animal(); --> ClassCastException类型转换异常
-
必须使用 instanceof 判断
Animal.java
public class Animal {
}
Dog.java
public class Dog extends Animal{
}
Cat.java
public class Cat extends Animal{
}
Test.java
public class Test {
public static void main(String[] args) {
//向上转型
Animal an = new Cat();
if (an instanceof Dog) {//判断引用an中的对象是否是Dog类
Dog dog = (Dog) an; //向下转型
System.out.println(dog);
}else if (an instanceof Cat) {//判断引用an中的对象是否是Cat类
Cat cat = (Cat) an; //向下转型
System.out.println(cat);
}
}
}
18. 内部类
在 Java 中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。
1. 成员内部类
可以调用外部类所有属性
成员内部类可以拥有 private、protected、public 访问权限及包访问权限。
个人理解:由于成员内部类看起来像是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。
Outter.java
//外部类
public class Outter {
private String str1 = "private属性";
String str2 = "default属性";
protected String str3 = "protected属性";
public String str4 = "public属性";
static String str5 = "static属性";
final String str6 = "final属性";
//成员内部类
public class Inner {
//如果内部类中有和外部类相同名字的属性或方法,就优先调用本类的(就近原则)
private String str1 = "成员内部类的同名属性";
public void method() {
System.out.println("成员内部类里的方法");
System.out.println(str1);
//如果要访问外部类的同名变量/方法, 外部类.this.成员变量/方法
System.out.println(Outter.this.str1);
System.out.println(str2);//Outter.this.str2
System.out.println(str3);//Outter.this.str3
System.out.println(str4);//Outter.this.str4
//静态属性直接类名调用
System.out.println(str5);//Outter.str5
System.out.println(str6);//Outter.this.str6
}
}
}
Test.java
public class Test {
public static void main(String[] args) {
//创建成员内部类的语法
//注意:创建成员内部类会创建外部类对象
Outter.Inner inner = new Outter().new Inner();
inner.method();
}
}
成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。
如果内部类中有和外部类相同名字的属性或方法,就优先调用本类的(就近原则)
如果要访问外部类的同名变量/方法,访问形式:外部类.this.成员变量/方法
2. 静态内部类
只能调用外部类的静态属性
Outter.java
//外部类
public class Outter {
private String str1 = "private属性";
static String str5 = "static属性";
//静态内部类
public static class Inner {
public void method() {
System.out.println("静态内部类里的方法");
//静态内部类不能调用外部类的非静态属性
// System.out.println(str1);
System.out.println(str5);//Outter.str5
}
}
}
Test.java
public class Test {
public static void main(String[] args) {
//创建静态内部类的语法
//注意:创建静态内部类不会创建外部类对象
Outter.Inner inner = new Outter.Inner();
inner.method();
}
}
静态内部类是不需要依赖于外部类的,并且它不能使用外部类的非static成员变量或方法,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。
3. 接口内部类
接口内部类 类似 静态内部类
IOutter.java
public interface IOutter {
//接口内部类
//默认添加static
public class Inner {
public void method() {
System.out.println("接口内部类的方法");
}
}
}
Test.java
public class Test {
public static void main(String[] args) {
//创建接口内部类对象的语法
IOutter.Inner inner = new IOutter.Inner();
inner.method();
}
}
4. 匿名内部类
匿名内部类是平时我们编写代码时用得最多的,在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加容易维护。
匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
使用场景:抽象类的子类对象只使用一次,就用匿名内部类
使用场景:接口的实现类对象只使用一次,就用匿名内部类
A.java
public abstract class A {
public abstract void method();
}
I1.java
public interface I1 {
public void method();
}
Test.java
public class Test {
public static void main(String[] args) {
//匿名内部类
//1.创建匿名子类继承A,重写method
//2.创建匿名子类的对象赋值给父类的引用(类的多态)
A a = new A(){
@Override
public void method() {
}
};
//匿名内部类
//1.创建匿名类,实现I1接口中method
//2.创建匿名类的对象赋值给接口的引用(接口的多态)
I1 i1 = new I1() {
@Override
public void method() {
}
};
}
}
匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。
匿名内部类在编译的时候由系统自动起名为 Outter$1.class。
5. 局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
局部内部类就像是方法里面的一个局部变量一样,是不能有 public、protected、private 以及 static 修饰符的。
Outter.java
//外部类
public class Outter {
public String method() {
//如果局部内部类中使用了该变量,JDK1.8开始会给该变量自动使用final修饰
int i = 100;
//局部内部类 在外部类的方法里
class Inner {
public String innerMethod() {
System.out.println("局部内部类的方法");
//局部内部类中使用了外部类的局部变量,该局部变量会自动使用final修饰该变量变成常量
return "使用了外部类的局部变量:" + i;
}
}
//创建局部内部类的对象
Inner inner = new Inner();
//调用局部内部类的方法
return inner.innerMethod();
}
}
Test.java
public class Test {
public static void main(String[] args) {
Outter outter = new Outter();
//调用外部类的方法时,自动调用了局部内部类的方法
System.out.println(outter.method());
}
}
Java Jdk1.8之后 局部内部类访问外部final变量问题
从内存中看,当方法里的局部变量所在的方法结束时,该变量即在栈内存中消失;
而内部类其实是一个类,只有内存中对它的所有引用都消失后,该内部类才“死亡”,即内部类的生命> 周期比局部变量长。
在多线程中,当方法结束后,局部内部类还在使用局部变量。
为了避免方法内的变量脱离方法而存在的现象发生,于是java规定局部内部类不能访问一般的局部> 变量。但能访问被final修饰的局部变量