面向对象进阶总结(static关键字&继承&多态&包&权限修饰符&final关键字&抽象类&接口&内部类)

面向对象进阶总结

复习回顾

1. 如何定义一个类

修饰符 class 类名 {
    // 1.成员变量(属性)
    // 2.成员方法 (行为) 
    // 3.构造方法 (初始化类的对象数据的)
}

2. 如何通过一个类来创建对象

类名 对象名称 = new 类名();

Student stu = new Student();

3. 封装

1. 封装的步骤
  1. 利用private关键字来修饰成员变量
  2. 利用public来修饰get和set方法
2. 封装步骤的实现
  1. private修饰成员变量

     ```java
     public class Student {
         private String name;
         private int age;
     }
     ```

     2. public修饰get和set方法

4. 构造方法

5. this关键字

1. this关键字的作用
  this就代表所在类当前对象的地址值,即当前对象
2. this关键字的应用
  1. 用于普遍的get和set方法
  2. 用于构造方法

一、 static关键字

1. 定义格式和使用

static是静态的意思。 static可以修饰成员变量或者修饰方法

2. 静态变量及其访问

有static修饰成员变量,说明他是属于类的,这个成员变量成为类变量或者静态成员变量。直接用类名访问。

public class Student {
    String name; //实例的成员变量
    int age;
    static String teacherName; //静态成员变量
    } 
    Student.teacherName = "gzk";//直接用类名调用即可

ps:因为类只有一个,所以静态成员变量内存区域中也只存在一份。所有的对象都可以共享这个变量。

共享??

我们一个班的同学共同拥有一个班主任叫gzk,而我们每一个同学的teacher的成员变量中都是一样的班主任,在给成员赋值时,我们没有必要每一次都对teacher赋值。因此这个在书写成员变量时应该加上static关键字

static String teacherName; //静态成员变量

加上static关键字后,我们每一位同学就共享了这个变量。只需要赋一次值,对于每一个学生都是一样的。

3.实例变量及其访问

无static修饰的成员变量属于每个对象的, 这个成员变量叫实例变量,之前我们写成员变量就是实例成员变量。

需要注意的是:实例成员变量属于每个对象,必须创建类的对象才可以访问。

格式:对象.实例成员变量

代码见上

4.静态方法及其访问

有static修饰成员方法,说明这个成员方法是属于类的,这个成员方法称为类方法或者静态方法。 直接用 类名访问即可。因为类只有一个,所以静态方法在内存区域中也只存在一份。所有的对象都可以共享这个方法。

与静态成员变量一样,静态方法也是直接通过==类名.方法名称==即可访问。

public static void mehtod(){
        System.out.println("静态方法");
    }

Student.mehtod();//直接用类名调用

5.实例方法及其访问

无static修饰的成员方法属于每个对象的,这个成员方法也叫做实例方法

需要注意的是:实例方法是属于每个对象,必须创建类的对象才可以访问。

格式:对象.实例方法

二、 继承

1. 引入

  1. 学生类 属性:姓名,年龄 行为:吃饭,睡觉
  2. 老师类 属性:姓名,年龄,薪水 行为:吃饭,睡觉,教书
  3. 班主任 属性:姓名,年龄,薪水 行为:吃饭,睡觉,管理

在这里类中有许多相同或者类似的行为,每一个都写,很麻烦。这里要找关系做继承结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cof6hP6h-1689520570815)(C:\Users\郭giao\AppData\Roaming\Typora\typora-user-images\image-20230714220812131.png)]

2. 继承的格式

通过 extends 关键字,可以声明一个子类继承另外一个父类,定义格式如下:

class 父类 {
	...
}

class 子类 extends 父类 {
	...
}

需要注意:Java是单继承的,一个类只能继承一个直接父类,跟现实世界很像,但是Java中的子类是更加强大的。

但是支持多层继承(爷爷)

3. 小结

  1. 继承实际上是子类相同的属性和行为能够定义在父类中,子类特有属性和行为由自己定义,这样就实现了相同属性和行为的重复利用,提高了代码复用。
  2. 子类继承父类,就可以直接得到父类的成员方法和变量,那么是否可以继承所有成分呢??

4.子类不能够继承的内容

  1. 子类是否可以继承所有成分呢

不是,子类不能继承父类的构造方法。

why?

子类如果继承父类的构造方法,从方法名不和文件名(对象名字)一致性上就已经出错了。

正确:

public class Person {  //父类
    String name;
    int age;

    public Person() {
	System.out.println("父类的无参构造");
    }

    public Person(String name, int age) {
	this.name = name;
	this.age = age;
    }
}

public class Student extends Person {  //子类,继承父类
    public Student(){
	//子类构造方法中隐藏的super()去访问父类的无参构造
        super();//必须写在有效代码的第一行  ,默认访问了父类的无参构造
        System.out.println("子类的无参构造");
    }
    public Student(String name ,int age){
        super(name,age);
    }
}
  • 通过子类去访问父类中private成员变量,可用子类的对象去调用getter方法

值得注意的是子类可以继承父类的私有成员(成员变量,方法),只是子类无法直接访问而已,可以通过getter/setter方法访问父类的private成员变量。

public class Demo03 {
    public static void main(String[] args) {
        Zi z = new Zi();
        System.out.println(z.num1);
//		System.out.println(z.num2); // 私有的子类无法使用
        // 通过getter/setter方法访问父类的private成员变量
        System.out.println(z.getNum2());

        z.show1();
        // z.show2(); // 私有的子类无法使用
    }
}

class Fu {
    public int num1 = 10;
    private int num2 = 20;

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

    private void show2() {
        System.out.println("show2");
    }

    public int getNum2() {
        return num2;
    }

    public void setNum2(int num2) {
        this.num2 = num2;
    }
}

class Zi extends Fu {
}

5.继承后的特点

1.成员变量
1. 成员变量不重名

如果子类和父类的成员变量不重名,这时访问是没有影响的。

class Fu {
	// Fu中的成员变量
	int num = 5;
}
class Zi extends Fu {
	// Zi中的成员变量
	int num2 = 6;
  
	// Zi中的成员方法
	public void show() {
		// 访问父类中的num
		System.out.println("Fu num="+num); // 继承而来,所以直接访问。
		// 访问子类中的num2
		System.out.println("Zi num2="+num2);
	}
}
class Demo04 {
	public static void main(String[] args) {
        // 创建子类对象
		Zi z = new Zi(); 
      	// 调用子类中的show方法
		z.show();  
	}
}

演示结果:
Fu num = 5
Zi num2 = 6
2. 成员变量重名

如果子类和父类成员变量出现重名,访问受到影响

class Fu1 {
	// Fu中的成员变量。
	int num = 5;
}
class Zi1 extends Fu1 {
	// Zi中的成员变量
	int num = 6;
  
	public void show() {
		// 访问父类中的num
		System.out.println("Fu num=" + num);
		// 访问子类中的num
		System.out.println("Zi num=" + num);
	}
}
class Demo04 {
	public static void main(String[] args) {
      	// 创建子类对象
		Zi1 z = new Zi1(); 
      	// 调用子类中的show方法
		z1.show(); 
	}
}
演示结果:
Fu num = 6
Zi num = 6

这里代码中,我们在子类中定义了个show方法,在show方法里想要去访问父类和子类中的num。由于这个方法写在子类中,在成员变量重名的影响下,他会先去本类中(这里是子类)寻找num,如果找到则执行,如果没找到,则去父类中找。(这就是为什么输出了num都是6的原因)

怎么解决???

用到了super关键字

3. super访问父类的成员变量

子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用super 关键字,修饰父类成员变量,类似于之前学过的 this

需要注意的是:super代表的是父类对象的引用,this代表的是当前对象的引用。

  • 使用格式:
super.父类成员变量名
class Fu {
	// Fu中的成员变量。
	int num = 5;
}

class Zi extends Fu {
	// Zi中的成员变量
	int num = 6;
  
	public void show() {
        int num = 1;
      
        // 访问方法中的num
        System.out.println("method num=" + num);
        // 访问子类中的num
        System.out.println("Zi num=" + this.num);
        // 访问父类中的num
        System.out.println("Fu num=" + super.num);
	}
}

class Demo04 {
	public static void main(String[] args) {
      	// 创建子类对象
		Zi1 z = new Zi1(); 
      	// 调用子类中的show方法
		z1.show(); 
	}
}

演示结果:
method num=1
Zi num=6
Fu num=5
  • 小贴士:

Fu类中的成员是非私有的,我们可以直接访问

如果是私有的,我们就需要用到gette和setter方法了

2. 成员方法
1. 成员方法不重名

如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。

class Fu {
	public void show() {
		System.out.println("Fu类中的show方法执行");
	}
}
class Zi extends Fu {
	public void show2() {
		System.out.println("Zi类中的show2方法执行");
	}
}
public  class Demo05 {
	public static void main(String[] args) {
		Zi z = new Zi();
     	//子类中没有show方法,但是可以找到父类方法去执行
		z.show(); 
		z.show2();  //子类有show2方法直接执行
	}
}
2. 成员方法重名

如果子类父类中出现==重名的成员方法,则创建子类对象调用该方法的时候,子类对象优先调用自己的方法==。

class Fu {
	public void show() {
		System.out.println("Fu show");
	}
}
class Zi extends Fu {
	//子类重写了父类的show方法
	public void show() {
		System.out.println("Zi show");
	}
}
public class ExtendsDemo05{
	public static void main(String[] args) {
		Zi z = new Zi();
     	// 子类中有show方法,只执行重写后的show方法
		z.show();  // Zi show
	}
}
3. 构造方法
  1. 构造方法的名字和类名是一致的,所以子类无法继承父类的构造方法。
  2. 构造方法的作用是初始化对象成员的数据的。所以子类的初始化过程中,必须先执行父类的初始动作。子类的构造方法中默认有个super(),表示调用父类构造方法,父类成员变量初始化后,才能给子类使用(先有爸爸,再有儿子)

继承后子类构方法器特点:子类所有构造方法的第一行都会默认先调用父类的无参构造方法

class Person {
    private String name;
    private int age;

    public Person() {
        System.out.println("父类无参");
    }

    // getter/setter省略
}

class Student extends Person {
    private double score;

    public Student() {
        //super(); // 调用父类无参,默认就存在,可以不写,必须在第一行
        System.out.println("子类无参");
    }
    
     public Student(double score) {
        //super();  // 调用父类无参,默认就存在,可以不写,必须再第一行
        this.score = score;    
        System.out.println("子类有参");
     }

}

public class Demo07 {
    public static void main(String[] args) {
        Student s1 = new Student();
        System.out.println("----------");
        Student s2 = new Student(99.9);
    }
}

输出结果:
父类无参
子类无参
----------
父类无参
子类有参
  • 小结:
  • 子类构造方法执行的时候,都会在第一行默认先调用父类的无参构造方法一次
  • 子类构造方法的第一行都隐含了一个super()去调用父类的无参构造,super()可以省略不写。

6. 方法重写

1. 概述

方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现

2.使用场景和案例

子类继承父类的方法,但是父类方法不满足子类的需求,则需对子类的方法重写一个与父类名字相同的方法,以覆盖父类的方法。

public class Animal  {
    public void run(){
        System.out.println("动物跑的很快!");
    }
    public void cry(){
        System.out.println("动物都可以叫~~~");
    }
}

public class Cat extends Animal {
    public void cry(){
        System.out.println("我们一起学猫叫,喵喵喵!喵的非常好听!");
    }
}

public class Test {
	public static void main(String[] args) {
      	// 创建子类对象
      	Cat ddm = new Cat()// 调用父类继承而来的方法
        ddm.run();
      	// 调用子类重写的方法
      	ddm.cry();
	}
}
3. @Override 重写注解
  • @Override:注解,重写注解校验

  • 这个注解标记的方法,就说明这个方法必须是重写父类的方法,否则编译阶段报错。

  • 建议重写都加上这个注解,一方面可以提高代码的可读性,一方面可以防止重写出错!

    加上后的子类代码形式如下:

    public class Cat extends Animal {
         // 声明不变,重新实现
        // 方法名称与父类全部一样,只是方法体中的功能重写写了!
        @Override
        public void cry(){
            System.out.println("我们一起学猫叫,喵喵喵!喵的非常好听!");
        }
    }
    
4. 注意事项
  1. 方法重写是发生在子父类之间的关系
  2. 子类方法覆盖父类方法,必须要保证权限大于等于父类权限
  3. 子类方法覆盖父类方法,返回值的类型,函数名你,参数列表都要一模一样

7. 继承的特点

1. Java只支持单继承,不支持多继承。
// 一个类只能有一个父类,不可以有多个父类。
class A {}
class B {}
class C1 extends A {} // ok
// class C2 extends A, B {} // error
2. 一个类可以有多个子类。
// A可以有多个子类
class A {}
class C1 extends A {}
class C2 extends  A {}
3. 可以多层继承。
class A {}
class C1 extends A {}
class D extends C1 {}

顶层父类是Object类。所有的类默认继承Object,作为父类。

三、 多态

1.多态的形式

  • 多态是继封装,继承之后,面向对象的第三大特性
  • 多态是出现在继承或者实现关系中的
  • 多态体现的形式
父类类型 变量名=new 子类/实现类构造器;
	变量名.方法名();
  • 多态的前提:有继承关系,子类变量是可以赋值给父类类型的变量。例如Animal是一个动物类型,而Cat是一个猫类型。Cat继承了Animal,Cat对象也是Animal类型,自然可以赋值给父类类型的变量。

2. 多态的使用场景

如果没有多态,下图中register方法只能传递学生对象,但是Teacher和administrator也可以注册,但是他俩调用不了register方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j9sQ1fvC-1689520570816)(C:\Users\郭giao\AppData\Roaming\Typora\typora-user-images\image-20230715144358262.png)]

有了多态之后,方法的形参就可以定义为共同的父类Person

要注意的是:

  • 当一个方法的形参是一个类的时候,我们可以传递这个类的所有子类对象
  • 当一个方法的形参是一个接口的时候,我们可以传递这个接口所有的实现类对象。
  • 而且多态还可以根据传递不同的对象来调用不同类的方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cFrwbaNp-1689520570816)(C:\Users\郭giao\AppData\Roaming\Typora\typora-user-images\image-20230715144945349.png)]

代码示例:

父类:

public class Person {
    private String name;
    private int age;

    空参构造
	    带全部参数的构造
    get和set方法

    public void show() {
	System.out.println(name + ", " + age);
    }
}

子类1public class Administrator extends Person {
    @Override
    public void show() {
	System.out.println("管理员的信息为:" + getName() + ", " + getAge());
    }
}

子类2public class Student extends Person {

    @Override
    public void show() {
	System.out.println("学生的信息为:" + getName() + ", " + getAge());
    }
}

子类3public class Teacher extends Person {

    @Override
    public void show() {
	System.out.println("老师的信息为:" + getName() + ", " + getAge());
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
	//创建三个对象,并调用register方法

	Student s = new Student();
	s.setName("张三");
	s.setAge(18);


	Teacher t = new Teacher();
	t.setName("王建国");
	t.setAge(30);

	Administrator admin = new Administrator();
	admin.setName("管理员");
	admin.setAge(35);


	register(s);
	register(t);
	register(admin);


    }


    //这个方法既能接收老师,又能接收学生,还能接收管理员
    //只能把参数写成这三个类型的父类
    public static void register(Person p) {
	p.show();
    }
}

3.多态的定义和前提

多态: 是指同一行为,具有多个不同表现形式。

从上面案例可以看出,Cat和Dog都是动物,都是吃这一行为,但是出现的效果(表现形式)是不一样的。

前提

  1. 有继承或者实现关系
  2. 方法的重写(意义体现:不重写,还写个der多态)
  3. 父类引用指向子类对象(格式体现)

​ 父类类型:指子类对象继承的父类类型,或者实现类的父接口类型

4.多态的运行特点

调用成员变量时:编译看左边,运行也看左边。(多态调用成员变量的两也

调用成员方法时:编译看左边,运行看右边。(多态调用成员方法的先左后右

代码体现:

public class Test {
    public static void main(String[] args) {
	//创建对象(多态方式)
	//Fu f =new Zi();
	Animal a = new Dog();   //多态
	//调用成员变量:编译看左边,运行也看左边
	//编译看左边:javac编译代码的时候,会看左边的父类中有没有这个变量(name),如果没有,则报错
	//运行看左边:运行时,实际获取的时左边父类中成员变量的值
	System.out.println(a.name); //动物(父类的成员变量)

	//调用成员方法:编译看左边,运行看右边
	//编译看左边:在编译时,会看左边的父类是否有这个方法,如果没有则编译失败
	//运行看右边:在运行时,实际上运行的是子类中的方法
	a.show(); //Dog--show  (子类的方法)
    }
}

class Animal {  //父类
    String name = "动物";

    public void show() {
	System.out.println("Animal--show");
    }

}

class Dog extends Animal {  //子类
    String name = "狗";

    @Override
    public void show() {
	System.out.println("Dog---show");
    }
}

5. 多态的弊端

  • 我们已经知道多态编译阶段是看左边父类类型的,这样就会导致,在子类中的某些独有的功能就无法通过多态的写法访问了。

  • 多态的弊端:不能够访问子类的==独有(特有)==方法

    原因: 多态中调用方法,要先去父类中看有无此方法,如果无则不能运行

    这里了话,既然是子类 的特有方法,那么在父类中是肯定没有的。因此不能够调用到子类的特有方法。

  • 解决办法: 变回子类类型就可以了。

    • Dog d = (Dog) a;

      ((Dog)a).lookHome;

  • 细节: 转换的时候不能乱转,如果转错类型就会出错

    • Animal a = new Dog();
    • Cat c =(Cat)a; dog转cat就会出错
instanceof关键字
 //instanceof关键字
	if(a instanceof Dog){  //a是狗吗 是---true  不是--false
	    Dog d=(Dog)a;
	    ((Dog)a).lookHome();
	    }else if(a instanceof Cat){  //a是猫吗
	    Cat c=(Cat)a;
	    c.catchMouse();
	    }else{
	    System.out.println("没有这个类型无法转换");
	    }

6. 引用类型转换

1. 为什么要转型

多态的写法无法访问子类的特有功能了。

回顾基本数据类型转换:

  • 自动转换:范围小的转换为范围大的,自动完成
  • 强制转换:范围大的给范围小的。

多态的转型分为向上转型(自动)和向下转型(强制)两种。

2. 向上转型(自动转换)
  • 向上转型:多态本身是子类类型向父类类型向上转换(自动转换)的过程,这个过程是默认的。
    当父类引用指向一个子类对象时,便是向上转型。
    使用格式:
父类类型 变量名=new 子类类型();
	如:Animal a=new Cat();

原因是:父类类型相对与子类来说是大范围的类型,Animal是动物类,是父类类型。Cat是猫类,是子类类型。Animal类型的范围当然很大,包含一切动物。
所以子类范围小可以直接自动转型给父类类型的变量。

3. 向下转型(强制转换)
  • 向下转型:父类向子类转换的过程。——强制的

    一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。

    使用格式:

    子类类型 变量名=(子类类型)父类变量名;:Aniaml a=new Cat();
    	Cat c=(Cat)a;  
    
4. 案例演示

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用
子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型

转型演示,代码如下:

定义类:

abstract class Animal {
    abstract void eat();
}

class Cat extends Animal {
    public void eat() {
	System.out.println("吃鱼");
    }

    public void catchMouse() {
	System.out.println("抓老鼠");
    }
}

class Dog extends Animal {
    public void eat() {
	System.out.println("吃骨头");
    }

    public void watchHouse() {
	System.out.println("看家");
    }
}

定义测试类:

public class Test {
    public static void main(String[] args) {
	// 向上转型  
	Animal a = new Cat();
	a.eat();                // 调用的是 Cat 的 eat

	// 向下转型  
	Cat c = (Cat) a;
	c.catchMouse();        // 调用的是 Cat 的 catchMouse
    }
}
5. 转型的异常

转型的过程中,一不小心就会遇到这样的问题,请看如下代码:

public class Test {
    public static void main(String[] args) {
	// 向上转型  
	Animal a = new Cat();
	a.eat();               // 调用的是 Cat 的 eat

	// 向下转型  
	Dog d = (Dog) a;
	d.watchHouse();        // 调用的是 Dog 的 watchHouse 【运行报错】
    }
}

这段代码可以通过编译,但是运行时,却报出了 ClassCastException ,类型转换异常!这是因为,明明创建了Cat类型对象,运行时,当然不能转换成Dog对象的。

6. instanceof关键字

为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验,格式如下:

变量名 instanceof 数据类型
	如果变量属于该数据类型或者其子类类型,返回true。
	如果变量不属于该数据类型或者其子类类型,返回false

所以,转换前,我们最好先做一个判断,代码如下:

public class Test {
    public static void main(String[] args) {
	// 向上转型  
	Animal a = new Cat();
	a.eat();               // 调用的是 Cat 的 eat

	// 向下转型  
	if (a instanceof Cat) {
	    Cat c = (Cat) a;
	    c.catchMouse();        // 调用的是 Cat 的 catchMouse
	} else if (a instanceof Dog) {
	    Dog d = (Dog) a;
	    d.watchHouse();       // 调用的是 Dog 的 watchHouse
	}
    }
}
7. instanceof的新特性

JDK14的时候提出了新特性,把判断和强转合并成了一行

//新特性
//先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d
//如果不是,则不强转,结果直接是false
if(a instanceof Dog d){
	d.lookHome();
	}else if(a instanceof Cat c){
	c.catchMouse();
	}else{
	System.out.println("没有这个类型,无法转换");
	}

四、 包

1.包

包名的命名规范

域名.路径名.xxx.xxx
// 例如:com.itheima.oa
  • 包名一般是公司域名的倒写。例如:黑马是www.itheima.com,包名就可以定义成com.itheima.技术名称。
  • 包名必须用”.“连接。
  • 包名的每个路径名必须是一个合法的标识符,而且不能是Java的关键字。

2. 导包

什么时候需要导包?

情况一:在使用Java中提供的非核心包中的类

情况二:使用自己写的其他包中的类

什么时候不需要导包?

情况一:在使用Java核心包(java.lang)中的类

情况二:在使用自己写的同一个包中的类

3. 使用不同包下相同的类怎么办?

假设demo1和demo2中都有一个Student该如何使用?

代码示例:

//使用全类名的形式即可。
//全类名:包名 + 类名
//拷贝全类名的快捷键:选中类名crtl + shift + alt + c 或者用鼠标点copy,再点击copy Reference
com.itheima.homework.demo1.Student s1=new com.itheima.homework.demo1.Student();
com.itheima.homework.demo2.Students2=new com.itheima.homework.demo2.Student();

五、 权限修饰符

1. 权限修饰符

在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限,我们之前已经学习过了public 和
private,接下来我们研究一下protected和默认修饰符的作用。

  • public:公共的,所有地方都可以访问。

  • protected:本类 ,本包,其他包中的子类都可以访问。

  • 默认(没有修饰符):本类 ,本包可以访问。

    注意:默认是空着不写,不是default

  • private:私有的,当前类可以访问。
    `public > protected > 默认 > private

2. 不同权限的访问能力

public protected 默认 private
同一类中
同一包中的类
不同包的子类
不同包中的无关类

可见,public具有最大权限。private则是最小权限。

编写代码时,如果没有特殊的考虑,建议这样使用权限:

  • 成员变量使用private ,隐藏细节。
  • 构造方法使用 public ,方便创建对象。
  • 成员方法使用public ,方便调用方法。

小贴士:不加权限修饰符,就是默认权限

六、 final关键字

1. 概述

学习了继承后,我们知道,子类可以在父类的基础上改写父类内容,比如,方法重写。

如果有一个方法我不想别人去改写里面内容,该怎么办呢?

Java提供了final 关键字,表示修饰的内容不可变。

  • final: 不可改变,最终的含义。可以用于修饰类、方法和变量。
    • 类:被修饰的不能被继承
    • 方法:被修饰的方法不能被重写
    • 变量:被修饰的变量,有且仅能被赋值一次
2. 使用方式
1.修饰类

final修饰的类,不能够被继承

格式如下:

final class 类名 {
}

代码:

final class Fu {
}
// class Zi extends Fu {} // 报错,不能继承final的类

查询API发现像 public final class Stringpublic final class Mathpublic final class Scanner
等,很多我们学习过的类,都是被final修饰的,目的就是供我们使用,而不让我们所以改变其内容。

2. 修饰方法

final修饰的方法,不可以被重写

格式如下:

修饰符 final 返回值类型 方法名(参数列表){
	//方法体
	}

代码:

class Fu2 {
    final public void show1() {
	System.out.println("Fu2 show1");
    }

    public void show2() {
	System.out.println("Fu2 show2");
    }
}

class Zi2 extends Fu2 {
    //	@Override
//	public void show1() {
//		System.out.println("Zi2 show1");
//	}
    @Override
    public void show2() {
	System.out.println("Zi2 show2");
    }
}
3.修饰变量
1.修饰局部变量

局部变量——基本类型
基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改。代码如下:

public class FinalDemo1 {
    public static void main(String[] args) {
	// 声明变量,使用final修饰
	final int a;
	// 第一次赋值 
	a = 10;
	// 第二次赋值
	a = 20; // 报错,不可重新赋值

	// 声明变量,直接赋值,使用final修饰
	final int b = 10;
	// 第二次赋值
	b = 20; // 报错,不可重新赋值
    }
}

思考,下面两种写法,哪种可以通过编译?

写法1:

final int c=0;
	for(int i=0;i< 10;i++){
	c=i;
	System.out.println(c);
	}

写法2:

for(int i=0;i< 10;i++){
final int c=i;
	System.out.println(c);
	}

根据 final 的定义,写法1报错!写法2,为什么通过编译呢?因为每次循环,都是一次新的变量c。这也是大家需要注意的地方。

2.修饰成员变量

成员变量涉及到初始化的问题,初始化方式有显示初始化和构造方法初始化,只能选择其中一个:

  • 显示初始化(在定义成员变量的时候立马赋值)(常用);
public class Student {
    final int num = 10;
}
  • 构造方法初始化(在构造方法中赋值一次)(不常用,了解即可)。

    注意:每个构造方法中都要赋值一次!

public class Student {
    final int num = 10;
    final int num2;

    public Student() {
	this.num2 = 20;
//     this.num2 = 20;
    }

    public Student(String name) {
	this.num2 = 20;
//     this.num2 = 20;
    }
}

被final修饰的常量名称,一般都有书写规范,所有字母都大写

七、 抽象类

1. 概述

父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有意义,而方法主体则没有存在的意义了(因为子类对象会调用自己重写的方法)。换句话说,父类可能知道子类应该有哪个功能,但是功能具体怎么实现父类是不清楚的(由子类自己决定),父类只需要提供一个没有方法体的定义即可,具体实现交给子类自己去实现。我们把没有方法体的方法称为抽象方法。Java语法规定,包含抽象方法的类就是抽象类

  • 抽象方法 : 没有方法体的方法。
  • 抽象类:包含抽象方法的类

2. abstract使用格式

abstract是抽象的意思,用于修饰方法方法和类,修饰的方法是抽象方法,修饰的类是抽象类。

1. 抽象方法

使用abstract 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。

定义格式:

修饰符 abstract 返回值类型 方法名 (参数列表)

代码举例:

public abstract void run()
2. 抽象类

如果一个类包含抽象方法,那么该类必须是抽象类注意:抽象类不一定有抽象方法,但是有抽象方法的类必须定义成抽象类。

定义格式:

abstract class 类名字 { 
  
}

代码举例:

public abstract class Animal {
    public abstract void run()}
3. 抽象类的使用

要求:继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。

代码举例:

// 父类,抽象类
abstract class Employee {
	private String id;
	private String name;
	private double salary;
	
	public Employee() {
	}
	
	public Employee(String id, String name, double salary) {
		this.id = id;
		this.name = name;
		this.salary = salary;
	}
	
	// 抽象方法
	// 抽象方法必须要放在抽象类中
	abstract public void work();
}

// 定义一个子类继承抽象类
class Manager extends Employee {
	public Manager() {
	}
	public Manager(String id, String name, double salary) {
		super(id, name, salary);
	}
	// 2.重写父类的抽象方法
	@Override
	public void work() {
		System.out.println("管理其他人");
	}
}

// 定义一个子类继承抽象类
class Cook extends Employee {
	public Cook() {
	}
	public Cook(String id, String name, double salary) {
		super(id, name, salary);
	}
	@Override
	public void work() {
		System.out.println("厨师炒菜多加点盐...");
	}
}

// 测试类
public class Demo10 {
	public static void main(String[] args) {
		// 创建抽象类,抽象类不能创建对象
		// 假设抽象类让我们创建对象,里面的抽象方法没有方法体,无法执行.所以不让我们创建对象
//		Employee e = new Employee();
//		e.work();
		
		// 3.创建子类
		Manager m = new Manager();
		m.work();
		
		Cook c = new Cook("ap002", "库克", 1);
		c.work();
	}
}

此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法

3. 抽象类的特征

抽象类的特征总结起来有得有失

有得:抽象类得到了拥有抽象方法的能力。

有失:抽象类失去了创建对象的能力。

其他成员(构造方法,实例方法,静态方法等)抽象类都是具备的。

4. 抽象类的细节

当idea报错时,知道怎么改即可

细节:

​ 1. 抽象类不能创建对象,如果创建,编译报错。只能创建其非抽象子类的对象

理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

  1. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。

  1. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类

理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。

  1. 抽象类子类,必须重写抽象父类中==所有的抽象方法==,否则子类也必须定义成抽象类,编译无法通过而报错。

理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。

  1. 抽象类存在的意义是为了被子类继承

理解:抽象类中已经实现的是模板中确定的成员,抽象类不确定如何实现的定义成抽象方法,交给具体的子类去实现。

5. 抽象类存在的意义

抽象类存在的意义是为了被子类继承,否则抽象类将毫无意义。抽象类可以强制让子类,一定要按照规定的格式进行重写。

八、 接口

1. 概述

我们已经学完了抽象类,抽象类中可以用抽象方法,也可以有普通方法,构造方法,成员变量等。那么什么是接口呢?接口是更加彻底的抽象,JDK7之前,包括JDK7,接口中全部是抽象方法。接口同样是不能创建对象的

2. 定义格式

//接口的定义格式:
interface 接口名称{
    // 抽象方法
}

// 接口的声明:interface
// 接口名称:首字母大写,满足“驼峰模式”

3. 接口成分的特点

在JDK7,包括JDK7之前,接口中的只有包含:抽象方法和常量

1. 抽象方法

注意:接口中的抽象方法默认会自动加上public abstract修饰程序员无需自己手写!!
按照规范:以后接口中的抽象方法建议不要写上public abstract。因为没有必要啊,默认会加上。

2. 常量

在接口中定义的成员变量默认会加上: public static final修饰。也就是说在接口中定义的成员变量实际上是一个常量。这里是使用public static final修饰后,变量值就不可被修改,并且是静态化的变量可以直接用接口名访问,所以也叫常量。常量必须要给初始值。常量命名规范建议字母全部大写,多个单词用下划线连接。

3. 案例演示
public interface InterF {
    // 抽象方法!
    //    public abstract void run();
    void run();

    //    public abstract String getName();
    String getName();

    //    public abstract int add(int a , int b);
    int add(int a , int b);


    // 它的最终写法是:
    // public static final int AGE = 12 ;
    int AGE  = 12; //常量
    String SCHOOL_NAME = "黑马程序员";

}

4. 基本实现

1. 实现接口的概述

类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements关键字

2. 实现接口的格式
/**接口的实现:
    在Java中接口是被实现的,实现接口的类称为实现类。
    实现类的格式:*/
class 类名 implements 接口1,接口2,接口3...{

}
3. 类实现接口的要求和意义
  1. 必须重写实现的全部接口中的所有抽象方法。
  2. 如果一个类实现了接口,但是没有重写完全部接口的全部抽象方法,这个类也必须定义成抽象类。

意义:接口体现的是一种规范,接口对实现类是一种强制性的约束,要么全部完成接口申明的功能,要么自己也定义成抽象类。这正是一种强制性的规范。

4. 类与接口基本实现案例

假如我们定义一个运动员的接口(规范),代码如下:

/**
   接口:接口体现的是规范。
 * */
public interface SportMan {
    void run(); // 抽象方法,跑步。
    void law(); // 抽象方法,遵守法律。
    String compittion(String project);  // 抽象方法,比赛。
}

接下来定义一个乒乓球运动员类,实现接口,实现接口的实现类代码如下:

package com.itheima._03接口的实现;
/**
 * 接口的实现:
 *    在Java中接口是被实现的,实现接口的类称为实现类。
 *    实现类的格式:
 *      class 类名 implements 接口1,接口2,接口3...{
 *
 *
 *      }
 * */
public class PingPongMan  implements SportMan {
    @Override
    public void run() {
        System.out.println("乒乓球运动员稍微跑一下!!");
    }

    @Override
    public void law() {
        System.out.println("乒乓球运动员守法!");
    }

    @Override
    public String compittion(String project) {
        return "参加"+project+"得金牌!";
    }
}

测试代码

public class TestMain {
    public static void main(String[] args) {
        // 创建实现类对象。
        PingPongMan zjk = new PingPongMan();
        zjk.run();
        zjk.law();
        System.out.println(zjk.compittion("全球乒乓球比赛"));

    }
}
5. 类与接口的多实现案例

类与接口之间的关系是多实现的,一个类可以同时实现多个接口。

首先我们先定义两个接口,代码如下:

/** 法律规范:接口*/
public interface Law {
    void rule();
}

/** 这一个运动员的规范:接口*/
public interface SportMan {
    void run();
}

然后定义一个实现类:

/**
 * Java中接口是可以被多实现的:
 *    一个类可以实现多个接口: Law, SportMan
 *
 * */
public class JumpMan implements Law ,SportMan {
    @Override
    public void rule() {
        System.out.println("尊长守法");
    }

    @Override
    public void run() {
        System.out.println("训练跑步!");
    }
}

从上面可以看出类与接口之间是可以多实现的,我们可以理解成实现多个规范,这是合理的。

5. 接口与接口的多继承

Java中,接口与接口之间是可以多继承的:也就是一个接口可以同时继承多个接口。大家一定要注意:

类与接口是实现关系

接口与接口是继承关系

接口继承接口就是把其他接口的抽象方法与本接口进行了合并。

案例演示:

public interface Abc {
    void go();
    void test();
}

/** 法律规范:接口*/
public interface Law {
    void rule();
    void test();
}

 *
 *  总结:
 *     接口与类之间是多实现的。
 *     接口与接口之间是多继承的。
 * */
public interface SportMan extends Law , Abc {
    void run();
}

6. 扩展: 接口的细节

  1. 两个接口存在相同抽象方法的时候,该怎么办?

只要重写一次即可。此时重写的方法,既表示重写1接口的,也表示重写2接口的。

  1. 实现类能不能继承A类的时候,同时实现其他接口呢?

继承的父类,就好比是亲爸爸一样
实现的接口,就好比是干爹一样
可以继承一个类的同时,再实现多个接口,只不过,要把接口里面所有的抽象方法,全部实现。

  1. 实现类能不能继承一个抽象类的时候,同时实现其他接口呢?

实现类可以继承一个抽象类的同时,再实现其他多个接口,只不过要把里面所有的抽象方法全部重写。

  1. 实现类Zi,实现了一个接口,还继承了一个Fu类。假设在接口中有一个方法,父类中也有一个相同的方法。子类如何操作呢?

处理办法一:如果父类中的方法体,能满足当前业务的需求,在子类中可以不用重写。
处理办法二:如果父类中的方法体,不能满足当前业务的需求,需要在子类中重写。

  1. 如果一个接口中,有10个抽象方法,但是我在实现类中,只需要用其中一个,该怎么办?

可以在接口跟实现类中间,新建一个中间类(适配器类)
让这个适配器类去实现接口,对接口里面的所有的方法做空重写
让子类继承这个适配器类,想要用到哪个方法,就重写哪个方法
因为中间类没有什么实际的意义,所以一般会把中间类定义为抽象的,不让外界创建对象

九、 内部类

1. 概述

1. 什么是内部类

将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。可以把内部类理解成寄生,外部类理解成宿主。

2. 什么时候使用内部类

一个事物内部还有一个独立的事物,内部的事物脱离外部的事物无法独立使用

B类表示的事物是A类的一部分,且B单独存在没有意义。

  1. 人里面有一颗心脏。
  2. 汽车内部有一个发动机。
  3. 为了实现更好的封装性。
  4. ArrayList的迭代器

2. 内部类的分类

按定义的位置来分

  1. 成员内部类,类定义在了成员位置 (类中方法外称为成员位置,无static修饰的内部类)
  2. 静态内部类,类定义在了成员位置 (类中方法外称为成员位置,有static修饰的内部类)
  3. 局部内部类,类定义在方法内
  4. 匿名内部类,没有名字的内部类,可以在方法中,也可以在类中方法外。

3. 成员内部类

成员内部类的特点:

  • 无static修饰内部类,属于外部类对象的
  • 宿主:外部类对象

内部类的使用格式:

外部类.内部类。 // 访问内部类的类型都是用 外部类.内部类

获取成员内部类对象的两种方式

方式一: 外部直接创建成员变量的对象

外部类.内部类  变量 = new 外部类().new 内部类();

方式一:
public class Test {
    public static void main(String[] args) {
        //  宿主:外部类对象。
       // Outer out = new Outer();
        // 创建内部类对象。
        Outer.Inner oi = new Outer().new Inner();
        oi.method();
    }
}

class Outer {
    // 成员内部类,属于外部类对象的。
    // 拓展:成员内部类不能定义静态成员。
    public class Inner{
        // 这里面的东西与类是完全一样的。
        public void method(){
            System.out.println("内部类中的方法被调用了");
        }
    }
}

方式二:在外部类中定义一个方法提供内部类的对象

方式二:
public class Outer {
    String name;
    private class Inner{
        static int a = 10;
    }
    public Inner getInstance(){
        return new Inner(); //返回内部类的对象
    }
}

public class Test {
    public static void main(String[] args) {
        Outer o = new Outer();
        System.out.println(o.getInstance());


    }
}

4. 成员内部类的细节

编写成员内部类的注意点:

  1. 成员内部类可以被一些修饰符所修饰,比如: private,默认,protected,public,static等
  2. 在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量。
  3. 创建内部类对象时,对象中有一个隐含的Outer.this记录外部类对象的地址值。(请参见3.6节的内存图)

详解:

​ 内部类被private修饰,外界无法直接获取内部类的对象,只能通过3.3节中的方式二获取内部类的对象

​ 被其他权限修饰符修饰的内部类一般用3.3节中的方式一直接获取内部类的对象

​ 内部类被static修饰是成员内部类中的特殊情况,叫做静态内部类下面单独学习。

​ 内部类如果想要访问外部类的成员变量,外部类的变量必须用final修饰,JDK8以前必须手动写final,JDK8之后不需要手动写,JDK默认加上。

5. 成员内部类面试题(内部类访问外部类对象)

请在?地方向上相应代码,以达到输出的内容

注意:内部类访问外部类对象的格式是:外部类名.this

public class Test {
    public static void main(String[] args) {
        Outer.inner oi = new Outer().new inner();
        oi.method();
    }
}

class Outer {	// 外部类
    private int a = 30;

    // 在成员位置定义一个类
    class inner {
        private int a = 20;

        public void method() {
            int a = 10;
            System.out.println(???);	// 10   答案:a
            System.out.println(???);	// 20	答案:this.a
            System.out.println(???);	// 30	答案:Outer.this.a
        }
    }
}

6. 成员内部类内存图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lyu3i3qP-1689520570817)(C:\Users\郭giao\AppData\Roaming\Typora\typora-user-images\image-20230715220214454.png)]

在内部对象002中,有一个Outer this 指向的地址是001(外部类),所以从内部类访问外部类可以通过这个。

7.静态内部类

1. 创建静态内部的对象的创建格式
外部类名.内部类名 对象名 = new 外部类名.内部类名();
  Outer.Inner       oi = new  Outer.Inner();
	oi.show1();
2. 调用静态方法的格式:
外部类名.内部类名.方法名();
    
Outer.Inner.show2();
3. 注意事项
  1. 静态内部类也是成员内部类的一种
  2. 静态内部类只能访问外部类中的静态变量静态方法(静态访问静态)
    如果想要访问非静态的需要创建对象

8. 局部内部类

定义格式
  • 局部内部类 :定义在方法中的类。

定义格式:

class 外部类名 {
	数据类型 变量名;
	
	修饰符 返回值类型 方法名(参数列表) {
		// …
		class 内部类 {
			// 成员变量
			// 成员方法
		}
	}
}
注意事项
  1. 将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。
  2. 外界是无法直接使用,需要在方法内部创建对象并使用
  3. 该类可以访问外部类的成员,也可以访问方法内的局部变量。
  • 内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。
	public class Outer {

    public void show(){
	int a = 10;//局部变量

	//局部内部类
	class Inner{
	    
	}
    }
}

能够修饰局部变量的,都能修饰局部内部类,比如final。。。。

  • 外界是无法直接使用,需要在方法内部创建对象并使用
public class Outer {

    public void show(){
	int a = 10;//局部变量

	//局部内部类
	class Inner{
	    String name;
	    int age;
	    
	    public void method1(){
		System.out.println("局部内部类中的method1");
	    }
	    
	    public static void method2(){
		System.out.println("局部内部类中的method2静态方法");
	    }
	    
	}
	//创建对象
	Inner i = new Inner();
	System.out.println(i.name);
	System.out.println(i.age);
	i.method1();
	Inner.method2();
    }
}
  • 该类可以访问外部类的成员,也可以访问方法内的局部变量。

    public class Outer {
        int b = 12;//外部
    
        public void show() {
    	int a = 10;//局部变量
    
    	//局部内部类
    	class Inner {
    	    String name;
    	    int age;
    	    
    	    public void method1() {
    		//访问外部
    		System.out.println(b);
    		
    		//方法内的局部变量
    		System.out.println(a)
    		
    		System.out.println("局部内部类中的method1");
    	    }
    	}
    	
        }
    }
    

9. 匿名内部类

1. 概述

**匿名内部类: ** 是内部类的简化写法,本质上就是隐藏了名字的内部类。(比较常用)

可以写在成员位置,或者局部位置

2. 格式
new 类名/接口名(){
	重写方法;
};

包含了:

  • 继承或者实现关系
  • 方法重写
  • 创建对象

从语法上看,这个整体其实是匿名内部类的对象

格式的细节:整体就是一个类的子类对象或者接口的实现类对象

3. 什么时候使用到匿名内部类

实际上,如果我们希望定义一个只要使用一次的类,就可以考虑匿名内部类。

匿名内部类的本质作用:简化代码

之前我们使用接口,要做一下操作:

  1. 定义子类

  2. 重写接口方法

  3. 创建子类对象

  4. 调用重写方法

interface Swim {
    public abstract void swimming();
}

// 1. 定义接口的实现类
class Student implements Swim {
    // 2. 重写抽象方法
    @Override
    public void swimming() {
        System.out.println("狗刨式...");
    }
}

public class Test {
    public static void main(String[] args) {
        // 3. 创建实现类对象
        Student s = new Student();
        // 4. 调用方法
        s.swimming();
    }
}

我们的目的,最终只是为了调用方法,那么能不能简化一下,把以上四步合成一步呢?匿名内部类就是做这样的快捷方式。

4. 匿名内部类前提和格式

匿名内部类必须继承一个父类或者实现一个接口

匿名内部类格式:

new 父类名或者接口名(){
    // 方法重写
    @Override 
    public void method() {
        // 执行语句
    }
};
5. 使用方式
  1. 原本是dog继承animal实现eat重写方法的。现在我们往方法中传递一个animal a,利用匿名内部类,将aniaml 重写成了dog的,再将这个类传给method();实现了多态
public class Animal {
    public void eat(){
	System.out.println("吃东西");
    }
}

public class Test {
    public static void main(String[] args) {
	/*
	new 类名或者接口名() {
                重写方法;
        };
	 */

	//Dog d= new Dog();     
	//d.eat();
	method(
		new Animal() {
		    @Override
		    public void eat() {
			System.out.println("重写狗吃吃");
		    }
		}
	);
    }

    public static void method(Animal a) {  //Animal a = 子类对象 多态
	a.eat();//编译看左边,运行看右边
    }

}
  1. 接口的多态
package 面向对象进阶.内部类.匿名内部类;

public class Test2 {
    public static void main(String[] args) {

	Swim s = new Swim() {
	    @Override
	    public void swim() {
		System.out.println("重写之后的swim方法");
	    }
	};
	s.swim();  // 接口的多态

	new Swim() {
	    @Override
	    public void swim() {
		System.out.println("重写之后的swim方法");
	    }
	}.swim();
	//对象.方法

    }
}

}

  }

}




### 9. 匿名内部类

#### 1. 概述

**匿名内部类: ** 是内部类的简化写法,本质上就是隐藏了名字的内部类。(比较常用)

可以写在成员位置,或者局部位置

#### 2. 格式

```java
new 类名/接口名(){
  重写方法;
};

包含了:

  • 继承或者实现关系
  • 方法重写
  • 创建对象

从语法上看,这个整体其实是匿名内部类的对象

格式的细节:整体就是一个类的子类对象或者接口的实现类对象

3. 什么时候使用到匿名内部类

实际上,如果我们希望定义一个只要使用一次的类,就可以考虑匿名内部类。

匿名内部类的本质作用:简化代码

之前我们使用接口,要做一下操作:

  1. 定义子类

  2. 重写接口方法

  3. 创建子类对象

  4. 调用重写方法

interface Swim {
    public abstract void swimming();
}

// 1. 定义接口的实现类
class Student implements Swim {
    // 2. 重写抽象方法
    @Override
    public void swimming() {
        System.out.println("狗刨式...");
    }
}

public class Test {
    public static void main(String[] args) {
        // 3. 创建实现类对象
        Student s = new Student();
        // 4. 调用方法
        s.swimming();
    }
}

我们的目的,最终只是为了调用方法,那么能不能简化一下,把以上四步合成一步呢?匿名内部类就是做这样的快捷方式。

4. 匿名内部类前提和格式

匿名内部类必须继承一个父类或者实现一个接口

匿名内部类格式:

new 父类名或者接口名(){
    // 方法重写
    @Override 
    public void method() {
        // 执行语句
    }
};
5. 使用方式
  1. 原本是dog继承animal实现eat重写方法的。现在我们往方法中传递一个animal a,利用匿名内部类,将aniaml 重写成了dog的,再将这个类传给method();实现了多态
public class Animal {
    public void eat(){
	System.out.println("吃东西");
    }
}

public class Test {
    public static void main(String[] args) {
	/*
	new 类名或者接口名() {
                重写方法;
        };
	 */

	//Dog d= new Dog();     
	//d.eat();
	method(
		new Animal() {
		    @Override
		    public void eat() {
			System.out.println("重写狗吃吃");
		    }
		}
	);
    }

    public static void method(Animal a) {  //Animal a = 子类对象 多态
	a.eat();//编译看左边,运行看右边
    }

}
  1. 接口的多态
package 面向对象进阶.内部类.匿名内部类;

public class Test2 {
    public static void main(String[] args) {

	Swim s = new Swim() {
	    @Override
	    public void swim() {
		System.out.println("重写之后的swim方法");
	    }
	};
	s.swim();  // 接口的多态

	new Swim() {
	    @Override
	    public void swim() {
		System.out.println("重写之后的swim方法");
	    }
	}.swim();
	//对象.方法

    }
}

你可能感兴趣的:(java,java,intellij,idea,学习方法)