面向对象(下)

snzjz / 2024-10-25 / 原文

面向对象(下)

1、继承

继承概述:
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
通过extends关键字可以实现类与类的继承
class 子类名 extends 父类名 {}  
单独的这个类称为父类,基类或者超类;这多个类可以称为子类或者派生类。
有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员。

    
继承的好处:
提高了代码的复用性
多个类相同的成员可以放到同一个类中
提高了代码的维护性
如果功能的代码需要修改,修改一处即可
让类与类之间产生了关系,是多态的前提
其实这也是继承的一个弊端:类的耦合性很强

继承的特点:
Java只支持单继承,不支持多继承。
一个类只能有一个父类,不可以有多个父类。
class SubDemo extends Demo{} //ok
class SubDemo extends Demo1,Demo2...//error
Java支持多层继承(继承体系)
class A{}
class B extends A{}
class C extends B{}


注意事项:
子类只能继承父类所有非私有的成员(成员方法和成员变量)
其实这也体现了继承的另一个弊端:打破了封装性
子类不能继承父类的构造方法,但是可以通过super(后面讲)关键字去访问父类构造方法。
不要为了部分功能而去继承

    
/*
    我想养一只🐕,所以先创建一个Dog类,里面有姓名和年龄的属性,吃饭和睡觉的方法
    我还想养一只🐱,猫也有姓名和年龄,也有吃饭和睡觉的方法,按照之前的做法,我们可以按照Dog类的写法再写一遍Cat类
    若我们还想养其它的动物的话,每个动物都有自己的名字和年龄,以及都会吃饭和睡觉,如果我们每一个类都写一遍的话,
    就会觉得代码重复度非常的高。
    java替我们考虑到了,我们可以使用另外一个A类,将重复的内容放到这个A类中,让B类和C类与A类,产生一个关系,
    关系一旦建立,B类和C类中,也就拥有了A类中的内容。这个关系,就是继承。
    java提供了一个关键字用来表示继承关系:extends
    写法:class B extends A{} 表示B类继承自A类
        这里的B称之为:子类,派生类
        这里的A称之为:父类,基类或者超类

    以后开发功能的时候,提倡高内聚,低耦合。
 */
class Animal{
    String name;
    int age;

    public void eat(){
        System.out.println("吃饭");
    }

    public void sleep(){
        System.out.println("睡觉");
    }
}

class Dog extends Animal {
    //子类相当于这里有一份父类中的非私有成员

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

class Cat extends Animal{

}

public class ExtendsDemo1{
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat();
        dog.sleep();
        System.out.println(dog.name);
        System.out.println(dog.age);
    }
}


============================================================
/*
    java中,类与类之间继承的时候,不允许一次继承多个类,只允许单继承,但是可以进行多重继承,形成继承体系
 */
class Mother{

}

class GrandFather{

}

class Father extends GrandFather{

}

class Son extends Father{

}


//class Son extends Mother,Father{
//
//}

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

    }
}
============================================================
/*
    使用继承的注意事项:
        1、子类无法继承父类中私有的成员
        2、子类不能继承父类的构造方法
        3、不要为了部分功能而去继承

    class A{
        fun1();
        fun2();
        fun3();
        fun4();
    }

    class B{
        fun4();
        fun5();
        fun6();
    }

    //不太适合使用继承改写
    class Fu{
        fun4();
    }

    class A extends Fu{
        fun1();
        fun2();
        fun3();
    }

    class B extends Fu{
        fun5();
        fun6();
    }

    问题:什么时候使用继承的思想,什么时候不用?
      答:当符合英语语法中的 is a的时候,就可以使用

 */
class Fu1{
    int a = 10;
    private int b = 20;


    Fu1(){
        System.out.println("这是父亲的无参构造方法");
    }

    public void show1(){

    }

    private void show2(){

    }
}

class Son1 extends Fu1{

    public void fun1(){
        System.out.println(a);
//        System.out.println(b);
        show1();
//        show2();


    }
}

public class ExtendsDemo3 {
    public static void main(String[] args) {
        Son1 son1 = new Son1();
        System.out.println(son1.a);
//        System.out.println(son1.b);
    }
}

============================================================

/*
    子类寻找成员变量并使用的流程:
        1、先就近原则,在方法内部中查找一次,若找到就使用
        2、若方法内部没有需要的变量,去当前类成员变量的位置上查找,若找到就使用
        3、若当前类成员变量的位置上没有需要的变量,去父类中的成员变量的位置上查找,若找到就使用
        4、若上面3个地方都没有找到所需要的变量,就报错

    java中提供了另外一个关键字super,用法跟this很像,但是super代表的是当前子类的直接父类的引用
    在子类中,可以通过super关键字使用直接父类中的非私有成员

 */
class Fu2{
    int a = 10;
}

class Son2 extends Fu2{
    int a = 20;

    public void fun1(){
        int a = 30;
        System.out.println(a); // 30
        System.out.println(this.a); // 20
//        Fu2 fu2 = new Fu2();
//        System.out.println(fu2.a);
        System.out.println(super.a); // 10


    }
}

public class ExtendsDemo4 {
    public static void main(String[] args) {
        Son2 son2 = new Son2();
        son2.fun1();
    }
}    
    
============================================================    
/*
    super关键字的使用注意事项:
        1、super仅代表当前类中的直接父类,无法使用super获取父类的父类中的成员
        2、super后面不能再调用super
 */

class GrandFu3{
    int a = 10;
}

class Fu3 extends GrandFu3{
     int a = 30;
}

class Son3 extends Fu3{
    int a = 20;
    public void fun1(){
        System.out.println(a);
        System.out.println(super.a);
//        System.out.println(super.super.a);
        GrandFu3 grandFu3 = new GrandFu3();
        System.out.println(grandFu3.a);
    }
}

public class ExtendsDemo5 {
    public static void main(String[] args) {
        Son3 son3 = new Son3();
        son3.fun1();
    }
}
    
============================================================    
/*
    继承和构造方法的关系
    在java继承关系中,要想初始化子类,必须先初始化其父类,这里默认super()体现的就是这个意思

    1、子类构造方法的第一句话,默认会有一个super(),对父类做初始化,只能在子类构造方法第一句出现。
    问题:如果父类中没有无参构造方法呢?
        1)使用super关键字调用父类中的有参构造方法,完成父类的初始化
        2)调用当前类中其它有参的构造方法间接对父类做初始化
    2、java中每一个只允许初始化一次


 */
class Fu4{
//    Fu4(){
//        System.out.println("这是父类中的无参构造方法.....");
//    }

    Fu4(String s){
        System.out.println("这是父类中有参数的构造方法....");
    }
}

class Son4 extends Fu4{
    Son4(){
        //super()
        super("数加");
//        this("数加");
        System.out.println("这是子类的无参构造方法.....");
//        super("数加");
    }

    Son4(String s){
        super(s);
    }
}

public class ExtendsDemo6 {
    public static void main(String[] args) {
        Son4 son4 = new Son4();
    }
}
    
    
============================================================    
 /*
    继承与成员方法的关系:
    当子类中的方法声明【方法的返回值类型,方法名和参数列表】与父类中的方法声明一致的时候,只是方法的实现不一样,这个现象叫做方法的重写【方法的覆盖】。
    java提供了一个注解帮助我们判断是否是重写@Override
 */

class Fu5{
    public void fun1(){
        System.out.println("这是父类中的fun1方法");
    }
}

class Zi5 extends Fu5{

    @Override
    public void fun1(){
        System.out.println("这是子类中的fun1方法");
    }


    public void fun2(){
        System.out.println("这是子类中的fun2方法");
    }
}

public class ExtendsDemo7 {
    public static void main(String[] args) {
        Zi5 zi5 = new Zi5();
        zi5.fun2();
        zi5.fun1();
    }
}
   
============================================================
    
/*
    重写所需要注意事项:
        1、子类无法重写父类中的私有方法
        2、子类无法继承且重写父类中的静态方法
        3、子类重写方法的权限,要大于等于父类的方法权限,推荐在没有要求的情况下,保持和父亲一致的权限就好了

    面试题:重写和重载什么区别?【overload和override什么区别?】
        1、重写是发生在继承关系中的,指的是子类方法的返回值类型和方法名以及参数列表与父类方法一致,只是实现不一样
        2、重载是发生在同一个类中,值的是方法名一样,参数列表不同,与返回值无关的现象
 */
class Fu6{
    public void fun1(){
        System.out.println("江川是世界上最帅的男人!");
    }

    void function1(){
        System.out.println("这是父类中默认修饰符的成员方法,...");
    }

    public static void method(){
        System.out.println("这是父类中的静态方法.....");
    }

    private void show1(){
        System.out.println("好好学习,天天向上!");
    }
}

class Zi6 extends Fu6{
    @Override
    public void fun1(){
        System.out.println("吴问强很不服,他认为自己才是世界上最帅的男人!");
    }

    @Override
    void function1(){
        System.out.println("这是子类中默认修饰符的成员方法,...");
    }

//    @Override
    public static void method(){
        System.out.println("这是父类中的静态方法.....");
    }

//    @Override
    private void show1(){
        System.out.println("在数加好好学习!");
    }
}

public class ExtendsDemo8 {
    public static void main(String[] args) {
        Zi6 zi6 = new Zi6();
        zi6.fun1();
    }
}
    
============================================================    
/*
    java提供了以关键字给我们使用,可以修饰父类成员方法,让其只能被子类使用,不能重写。
    final: 最终的,不可改变的

 */
class Fu7 {
    public final void fun1(){
        System.out.println("江川是世界上最帅且有钱的男人! ");
    }
}

class Zi7 extends Fu7 {
//    @Override
//    public void fun1(){
//        System.out.println("李刚是下一任最帅且有钱的男人!");
//    }
}

public class ExtendsDemo9 {
    public static void main(String[] args) {
        Zi7 zi7 = new Zi7();
        zi7.fun1();
    }
}    
    
 

2、super关键字

super的用法和this很像
this代表本类对应的引用。
super代表父类存储空间的标识(可以理解为父类引用)
用法(this和super均可如下使用)
访问成员变量
this.成员变量		super.成员变量
访问构造方法(子父类的构造方法问题讲)
this(…)		super(…)
访问成员方法(子父类的成员方法问题讲)
this.成员方法()	super.成员方法()

3、多态

1、多态概述:
某一个事物,在不同时刻表现出来的不同状态。
举例:
猫可以是猫的类型。猫 m = new 猫();
同时猫也是动物的一种,也可以把猫称为动物。
动物 d = new 猫();
在举一个例子:水在不同时刻的状态
多态前提和体现
有继承关系	
有方法重写	
有父类引用指向子类对象

/*
    多态:指的是某一个事物,在不同时刻下的不同状态
    举例:水【气态的水,液态的水,固态的水】
        水果【🍉,🍇,🍎】

    java实现多态有三要素:
        1、要有继承关系
        2、要有方法的重写
            子类可以不重写父类中的方法,但是重写了会更好的体现子类的特点
        3、要有父类的引用指向子类对象

 */
class Animal2{
    String name;
    int age;

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

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

class Dog2 extends Animal2{
    @Override
    public void eat(){
        System.out.println("🐕吃🥩");
    }

    @Override
    public void sleep(){
        System.out.println("🐕侧着睡");
    }
}

class Cat2 extends Animal2{
    @Override
    public void eat(){
        System.out.println("🐱吃🐟");
    }

    @Override
    public void sleep(){
        System.out.println("🐱蜷着睡");
    }
}


public class DuoTaiDemo1 {
    public static void main(String[] args) {
        //要有父类的引用指向子类对象
        Animal2 a1 = new Dog2(); // 类与类之间的多态

//        Dog2 d1 = new Cat2();
    }
}
============================================================    
    
2、多态成员访问特点
成员变量
编译看左边,运行看左边
成员方法
编译看左边,运行看右边
静态方法
编译看左边,运行看左边
所以前面我说静态方法不能算方法的重写

/*
    多态访问成员的特点:
        成员变量:编译看左,运行看左。
        成员方法:编译看左,运行看右。
        静态成员方法:编译看左,运行看左。
 */

class Fu10 {
    int a = 10;

    public void fun1() {
        System.out.println("好好学习,天天向上!");
    }

    public static void show1() {
        System.out.println("这是父类中的静态方法show1");
    }

}

class Zi10 extends Fu10 {
    int a = 20;

    @Override
    public void fun1() {
        System.out.println("在数加科技里好好学习!");
    }

    //    public void fun1() {
//        System.out.println("在数加科技里好好学习!");
//    }

    public static void show1() {
        System.out.println("这是子类中的静态方法show1");
    }
}

public class DuoTaiDemo2 {
    public static void main(String[] args) {
        Fu10 f = new Zi10();
        System.out.println(f.a);
        f.fun1();
        f.show1();
    }
}
============================================================
    
3、多态的好处:
提高了程序的维护性(由继承保证)
提高了程序的扩展性(由多态保证)
多态的弊端:
不能访问子类特有功能
那么我们如何才能访问子类的特有功能呢?
多态中的转型
    
/*
    多态的好处:
        1、提高了程序的维护性(由继承保证)
        2、提高了程序的扩展性(由多态保证)

 */
class Animal{
    public void eat(){
        System.out.println("吃");
    }

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

class Dog extends Animal{
    @Override
    public void eat() {
        System.out.println("🐕吃🥩");
    }

    @Override
    public void sleep() {
        System.out.println("🐕侧着睡");
    }
}

class Cat extends Animal{
    @Override
    public void eat() {
        System.out.println("🐱吃🐟");
    }

    @Override
    public void sleep() {
        System.out.println("🐱蜷着睡");
    }
}

class Sheep extends Animal{
    @Override
    public void eat() {
        System.out.println("🐏吃草");
    }

    @Override
    public void sleep() {
        System.out.println("🐏趴着睡");
    }
}

class Turtle extends Animal{
    @Override
    public void eat() {
        System.out.println("🐢吃🥩");
    }

    @Override
    public void sleep() {
        System.out.println("🐢缩着睡");
    }
}

class AnimalTool{
    public static void useAnimal(Animal animal){
        animal.eat();
        animal.sleep();
    }

//    public static void useCat(Cat cat){
//        cat.eat();
//        cat.sleep();
//    }
//
//    public static void useDog(Dog dog){
//        dog.eat();
//        dog.sleep();
//    }
//
//    public static void useSheep(Sheep sheep){
//        sheep.eat();
//        sheep.sleep();
//    }
}

public class DuoTaiDemo1 {
    public static void main(String[] args) {
        //现在我想养一只🐕
        Dog d1 = new Dog();
//        d1.eat();
//        d1.sleep();
//        useDog(d1);
//        AnimalTool.useDog(d1);
        AnimalTool.useAnimal(d1);
        Dog d2 = new Dog();
//        d2.eat();
//        d2.sleep();
//        useDog(d2);
//        AnimalTool.useDog(d2);
        AnimalTool.useAnimal(d2);

        //我现在不想养🐕,我想养一只🐱
        Cat c1 = new Cat();
//        c1.eat();
//        c1.sleep();
//        useCat(c1);
//        AnimalTool.useCat(c1);
        AnimalTool.useAnimal(c1);

        //随着我们养的动物种类越来越多,我们发现
        //1. 自定义动物的类越来越多【这是不可避免】
        //2. 当前类中的useXxx的方法越来越多,写在这里其实并不合适,因为这是一个测试类
        //测试类中主要涉及创建对象调用功能
        //我们可以将调用动物功能的方法放到一个动物工具类中
        //我想养一只🐏
        Sheep s1 = new Sheep();
//        AnimalTool.useSheep(s1);
        AnimalTool.useAnimal(s1);

        //工具类是不应该频繁被修改的类,也就是说,我们写好一个工具类后,即便我们有新的动物出现,也不需要修改工具类也可以使用
        //利用多态的扩展性来使用
        //我想养一只🐢
        Turtle t1 = new Turtle();
        AnimalTool.useAnimal(t1);


    }

//    public static void useCat(Cat cat){
//        cat.eat();
//        cat.sleep();
//    }
//
//    public static void useDog(Dog dog){
//        dog.eat();
//        dog.sleep();
//    }
}
============================================================
    
4、多态中的转型问题
向上转型
从子到父
父类引用指向子类对象
向下转型
从父到子
父类引用转为子类对象

曹操和曹植的故事
曹操和曹植是父子关系
class 曹操{
    public void fun1(){
        带兵打仗
    }
}

class 曹植 extends 曹操{
    @Override
    public void fun1(){
        下棋
    }

    public void xieShi(){
        写诗
    }
}

有一天,曹操带兵打仗出去了,这时候,刘备来攻打城池,假设城中的士兵只听曹操的指挥,但是现在只剩下曹植
曹植想到一个办法,开始装爹. 穿上爹的衣服,粘上假胡子.
曹操 c1 = new 曹植();
c1.fun1();
//c1.xieShi();
当曹操回来的时候,曹植脱下爹的衣服,撕掉假胡子,做回自己
曹植 c2 = (曹植)c1;
c2.fun1();
c2.xieShi();

============================================================
    
class Fu1{
    public void fun1(){
        System.out.println("好好学习,天天向上!");
    }
}

class Zi1 extends Fu1{
    @Override
    public void fun1() {
        System.out.println("李刚现在在数加好好学习,天天向上!");
    }

    public void show1(){
        System.out.println("李刚现在没有睡觉...");
    }
}

class Demo1 extends Fu1{

}

public class DuoTaiDemo2 {
    public static void main(String[] args) {
        Fu1 f1 = new Zi1();
        f1.fun1();
//        f1.show1();
        //向下转型
        //格式: 子类类名 变量名 = (子类类名)要转型的变量名;
        Zi1 z1 = (Zi1)f1;
        z1.show1();
        //并不是任意两个类型之间都可以做向下转型,只有实际内存对象类型与要转的类型一样
//        Demo1 d1 = (Demo1) f1; //ClassCastException

    }
}

4、抽象类

1、抽象类特点:
抽象类和抽象方法必须用abstract关键字修饰
格式
abstract class 类名 {}
public abstract void eat();
抽象类不一定有抽象方法,有抽象方法的类一定是抽象类
抽象类不能实例化
那么,抽象类如何实例化呢?
按照多态的方式,由具体的子类实例化。其实这也是多态的一种,抽象类多态。
抽象类的子类
要么是抽象类
要么重写抽象类中的所有抽象方法

    
2、抽象类的成员特点:
成员变量
可以是变量
也可以是常量
构造方法
有构造方法,但是不能实例化
那么,构造方法的作用是什么呢?
用于子类访问父类数据的初始化
成员方法
可以有抽象方法 限定子类必须完成某些动作
也可以有非抽象方法 提高代码服用性
    
    
3、一个类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?
在 Java 中,一个类如果没有抽象方法,理论上是可以被定义为抽象类的。然而,这样的抽象类在功能上与普通类并没有太大的区别,因为抽象类的主要目的是为了强制子类实现某些抽象方法。    

    
4、abstract不能和哪些关键字共存
private	冲突
final	冲突	
static	无意义
    
    
/*
    java为了表示现实生活中抽象的概念集合,提供了一个关键字给我们使用:abstract
    abstract 抽象的
    可以修饰类,修饰成员方法

    1. 被abstract修饰的类是抽象类, 抽象类不能被实例化
    2. 被abstract修饰的方法是抽象方法, 抽象方法不能有大括号实现
    3. 在抽象类,既可以存在具体实现的方法, 也可以存在抽象方法
    4. 若一个类中有抽象方法, 这个类一定是一个抽象类
    5. 当一个具体的类继承一个抽象类, 必须要实现抽象类中的所有抽象方法
    6. 当一个抽象类继承一个抽象类的时候, 可以选择性地是否重写抽象方法

 */
abstract class Animal2{
    //拥有具体实现的方法
    public void eat1(){
        System.out.println("吃饭");
    }

    //抽象方法
    public abstract void eat2();
}

abstract class A1 extends Animal2{

}


class Dog2 extends Animal2{
    @Override
    public void eat2() {
        System.out.println("🐕吃🥩");
    }
}


public class AbstractDemo1 {
    public static void main(String[] args) {
//        Animal2 animal2 = new Animal2();
    }
}
    
============================================================  
/*
    抽象类与类中成员的关系:
        成员变量: 抽象类既可以存在变量, 也可以存在常量
        构造方法: 可以存在构造方法, 是为了将来在继承关系做初始化的作用
        成员方法: 既可以是具体的实现方法, 也可以是抽象方法

 */
abstract class Demo2{
//    int a = 10;
//    final int b = 20;

    Demo2(){}
}

class A2 extends Demo2{
    A2(){
//        super();
    }
}

public class AbstractDemo2 {
    public static void main(String[] args) {
//        Demo2 demo2 = new Demo2();
    }
}
============================================================    
/*
   1. 一个类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义? 可以表示一种概念的集合
   2. abstract不能和哪些关键字共存
        final 不能共存
        static 不能共存
        private 不能共存



    到目前为止,我们学过哪些修饰词关键字
    final
    static
    private
    abstract
 */

abstract class ShuJia{
//     abstract final void fun1(); // 非法的修饰符组合: abstract和final

//    abstract static void fun1(); // 非法的修饰符组合: abstract和static

//    private abstract void fun1(); // 非法的修饰符组合: abstract和private
}

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

    }
}    

5、接口

1、接口特点:
接口用关键字interface表示
格式:interface 接口名 {}
类实现接口用implements表示
格式:class 类名 implements 接口名 {}
接口不能实例化
那么,接口如何实例化呢?
按照多态的方式,由具体的子类实例化。其实这也是多态的一种,接口多态。
接口的子类
要么是抽象类
要么重写接口中的所有抽象方法

成员变量
只能是常量
默认修饰符 public static final
构造方法
没有,因为接口主要是扩展功能的,而没有具体存在
成员方法
只能是抽象方法
默认修饰符 public abstract
成员变量
只能是常量
默认修饰符 public static final
构造方法
没有,因为接口主要是扩展功能的,而没有具体存在
成员方法
只能是抽象方法
默认修饰符 public abstract

2、类与类,类与接口以及接口与接口的关系
类与类
继承关系,只能单继承,但是可以多层继承
类与接口
实现关系,可以单实现,也可以多实现。还可以在继承一个类的同时实现多个接口
接口与接口
继承关系,可以单继承,也可以多继承
    
    
抽象类和接口的区别:
成员区别
抽象类 变量,常量;有抽象方法;抽象方法,非抽象方法
接口 常量;抽象方法
关系区别
类与类 继承,单继承
类与接口 实现,单实现,多实现
接口与接口 继承,单继承,多继承
设计理念区别
抽象类 被继承体现的是:”is a”的关系。共性功能
接口 被实现体现的是:”like a”的关系。扩展功能
    
    
/*
    接口:表示一个类的额外功能的实现
    java提供了一个关键字表示接口:interface
    接口我们可以将它看作成一个特殊的类, 因为接口也会被编译成一个class文件

    接口注意事项:
        1. 接口中只能存在抽象方法, jvm默认会在方法前使用public abstract进行修饰, 刚学java推荐加上
        2. 类和接口是实现关系 可以通过关键字implements实现接口
 */

interface QiChe{
    public abstract void qiche();

//    public void fun1(){
//        System.out.println("好好学习");
//    }
}


abstract class Animal3{
    public abstract void eat();
}

class Bear extends Animal3{
    @Override
    public void eat(){
        System.out.println("🐻吃🥩");
    }
}

class QiCheBear extends Animal3 implements QiChe{
    @Override
    public void eat() {
        System.out.println("🐻吃🥩");
    }

    @Override
    public void qiche() {
        System.out.println("训练后的🐻会骑车");
    }
}

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

    }
}
    
============================================================    
/*
    接口:表示一个类的额外功能的实现
    java提供了一个关键字表示接口:interface
    接口我们可以将它看作成一个特殊的类, 因为接口也会被编译成一个class文件

    接口注意事项:
        1. 接口中只能存在抽象方法, jvm默认会在方法前使用public abstract进行修饰, 刚学java推荐加上
        2. 类和接口是实现关系 可以通过关键字implements实现接口
        3. 当一个具体的类实现一个接口的时候, 必须要实现接口中所有的抽象方法
        4. 若一个抽象类实现一个接口的时候,可以选择性地实现接口中的抽象方法
        5. 一个类可以同时实现多个接口,使用逗号隔开
        6. 接口和接口存在继承关系, 并且一个接口可以同时继承多个接口
        7.

   面试题:
        java中允许多继承吗?
        答:
            若是类和类之间的继承,只能单继承,不能多继承
            若是接口与接口之间的继承,可以多继承
 */



interface Inter{
    void fun1();
    void fun2();
}

interface Inter2 {
    void fun3();
}

interface Inter3 extends Inter,Inter2{
    //fun1();
    //fun2();
    //fun3();
    void fun4();
}



class Demo5 implements Inter,Inter2{

    @Override
    public void fun1() {

    }

    @Override
    public void fun2() {

    }

    @Override
    public void fun3() {

    }
}

abstract class Demo4 implements Inter{

}



class Demo1Impl implements Inter{

    @Override
    public void fun1() {

    }

    @Override
    public void fun2() {

    }
}

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

    }
}
    
============================================================/*
    接口:表示一个类的额外功能的实现
    java提供了一个关键字表示接口:interface
    接口我们可以将它看作成一个特殊的类, 因为接口也会被编译成一个class文件

    接口注意事项:
        1. 接口中只能存在抽象方法, jvm默认会在方法前使用public abstract进行修饰, 刚学java推荐加上
        2. 类和接口是实现关系 可以通过关键字implements实现接口
        3. 当一个具体的类实现一个接口的时候, 必须要实现接口中所有的抽象方法
        4. 若一个抽象类实现一个接口的时候,可以选择性地实现接口中的抽象方法
        5. 一个类可以同时实现多个接口,使用逗号隔开
        6. 接口和接口存在继承关系, 并且一个接口可以同时继承多个接口
        7. 接口中只能定义常量, 默认修饰符为public static final
        8. 接口无法实例化, 接口中不能出现构造方法

   面试题:
        java中允许多继承吗?
        答:
            若是类和类之间的继承,只能单继承,不能多继承
            若是接口与接口之间的继承,可以多继承
 */

interface Inter1{
    public static final int a = 10;

//    Inter1(){}
}

class Demo6 implements Inter1{
    public void fun1(){
//        a = 20;
        System.out.println(a);
    }

}

public class InterfaceDemo3 {
    public static void main(String[] args) {
//        Demo6 demo6 = new Demo6();
//        System.out.println(demo6.a);
//        System.out.println(Inter1.a);
//        demo6.fun1();

//        Inter1 inter1 = new Inter1();

        Inter1 i1 = new Demo6(); // 接口多态
    }
}    
    
 
    

6、形式参数 返回值

/*
    形式参数
        基本类型: 当基本数据类型作为参数传递的时候,传递是具体的数值
        引用类型:
            数组:当数组作为方法的参数类型的时候,将来需要传递数组的地址值
            具体的类:当你看到一个类作为方法的参数类型的时候,将来调用需要传入该类或该类的子类的对象
            抽象类:当你看到一个抽象类作为方法的参数类型的时候,将来调用需要传入该抽象类具体实现子类的对象
            接口:当你看到一个接口作为方法的参数类型的时候,将来调用需要传入实现了该接口的具体子类对象
 */
interface Inter1{
    void fun1();
}

class Student3 implements Inter1{
    @Override
    public void fun1() {
        System.out.println("钱志强是世界上最有钱的男人!");
    }
}

class StudentDemo3{
    //当你看到一个接口作为方法的参数类型的时候,将来调用需要传入实现了该接口的具体子类对象
    public void show1(Inter1 inter1){ //Inter1 inter1 = new Student3()
        inter1.fun1();
    }
}

public class Demo3 {
    public static void main(String[] args) {
        StudentDemo3 studentDemo3 = new StudentDemo3();
        studentDemo3.show1(new Student3());
    }
}

============================================================
/*
    返回值
        基本类型: 当基本数据类型作为方法的返回值类型的时候,方法中return具体类型的数值即可
        引用类型:
            数组:当数组作为方法的返回值类型的时候,将来需要在方法中return该一个数组的地址值
            具体的类:当类作为方法的返回值类型的时候,将来需要在方法中return该一个该类的对象
            抽象类:当抽象类作为方法的返回值类型的时候,将来需要在方法中return该一个该类的具体子类对象
            接口:当接口作为方法的返回值类型的时候,将来需要在方法中return该一个实现了该接口的具体子类对象
 */
interface Inter1{
    void fun1();
}

class Inter1Impl implements Inter1{
    @Override
    public void fun1() {
        System.out.println("好好学习,天天向上!");
    }
}

class Student3{
    //当接口作为方法的返回值类型的时候,将来需要在方法中return该一个实现了该接口的具体子类对象
    public Inter1 show1(){
        return new Inter1Impl();
    }
}


public class Demo3 {
    public static void main(String[] args) {
//        Student3 student3 = new Student3();
//        Inter1 i = student3.show1(); //Inter1 i = new Inter1Impl()
//        i.fun1();
//        Student3 s = new Student3();
//        Inter1 i = new Student3().show1(); //Inter1 i = new Inter1Impl()

        //当一个方法结果是一个对象的时候,可以继续.调用返回对象中的其它方法
        //像这种对象调用方法继续调用的编程方式,称之为叫做链式调用【链式编程】
        new Student3()
                .show1()
                .fun1();
    }
}

7、权限修饰符

		      public		protected		默认		private

同一类中		√				√				√ 			√

同一包子类,	   √			   √    		   √
其他类

不同包			√				√				
子类

不同包			√
其他类
                  
类:
默认,public,final,abstract
我们自己定义:public居多
成员变量:
四种权限修饰符均可,final,static
我们自己定义:private居多
构造方法:
四种权限修饰符均可,其他不可
我们自己定义:public 居多
成员方法:
四种权限修饰符均可,fianl,static,abstract
我们自己定义:public居多

8、内部类

内部类概述:
把类定义在其他类的内部,这个类就被称为内部类。
举例:在类A中定义了一个类B,类B就是内部类。
内部类的访问特点:
内部类可以直接访问外部类的成员,包括私有。
外部类要访问内部类的成员,必须创建对象。

/*
    内部类:在一个类中定义一个类
        根据定义的位置不同,分为两种内部类
        成员内部类:将类定义在类中成员的位置【类中方法外】上
            常见修饰成员内部类的修饰词:
                private
                static
        局部内部类:


 */
//class Outer{
//    //成员方法
//    int a = 10;
//    //成员方法
//    public void show1(){
//        System.out.println(this.a);
//    }
//
//    //成员内部类
//    class Inner{
//        int a = 20;
//        public void fun1(){
//            int a = 30;
//            System.out.println(a); // 30
//            System.out.println(this.a); // 20
//            System.out.println(Outer.this.a); // 10
//        }
//    }
//}

class Outer{
    //成员方法
    int a = 10;
    //成员方法
    public void show1(){
        System.out.println(this.a);
    }

    //成员内部类
//    private class Inner{
//        int a = 20;
//        public void fun1(){
//            int a = 30;
//            System.out.println(a); // 30
//            System.out.println(this.a); // 20
//            System.out.println(Outer.this.a); // 10
//        }
//    }

    static class Inner{
        int a = 20;
        public void fun1(){
            int a = 30;
            System.out.println(a); // 30
            System.out.println(this.a); // 20
//            System.out.println(Outer.this.a); // 10
        }
    }

//    public void show2(){
//        Inner inner = new Inner();
//        inner.fun1();
//    }


}

public class InnerClassDemo1 {
    public static void main(String[] args) {
        //创建成员内部类对象
        //外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
//        Outer.Inner inner = new Outer().new Inner();
//        inner.fun1();

        //若成员内部类使用static修饰的话,可以直接使用外部类名获取内部类的构造方法进行创建
        Outer.Inner inner = new Outer.Inner();

    }
}

============================================================

/*
    局部内部类:将一个类定义在一个方法的内部,只能在方法的内部使用

 */

class Outer2{

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

    public void show1(){
        int b = 11; // 若是被局部内部类使用的话,JDK1.8之后JVM默认会加上final
        //局部内部类
        class Inner{
            public void function(){
//                b = 22;
                System.out.println("好好学习,天天向上!");
//                System.out.println(b);
            }
        }
        b = 22;
        Inner inner = new Inner();
        inner.function();
    }
}

public class InnerClassDemo2 {
    public static void main(String[] args) {
        Outer2 outer2 = new Outer2();
        outer2.show1();
    }
}

============================================================

/*
    匿名内部类:
        new 接口名/抽象类名(){
            //重写接口或者抽象类中的方法
        }

    就意味着JVM在运行过程中做了几件事:
        1、JVM内部自己创建一个类
        2、这个类实现一个接口或者继承了一个抽象类
        3、将这个类的对象创建出来


 */
interface Inter1{
    void fun1();
}

//class Inter1Impl1 implements Inter1{
//    @Override
//    public void fun1() {
//        System.out.println("李刚今天下午没有睡觉...");
//    }
//}
//
//class Inter1Impl2 implements Inter1{
//    @Override
//    public void fun1() {
//        System.out.println("江川今天下午睡觉了...");
//    }
//}

class Demo1{
    //当你看到一个接口作为方法参数类型的时候,将来调用时需要传入实现该接口的具体类对象
    public void show1(Inter1 inter1){ //Inter1 inter1 = new Inter1Impl1()
        inter1.fun1();
    }
}

public class NiMingClassDemo1 {
    public static void main(String[] args) {
        Demo1 demo1 = new Demo1();
//        demo1.show1(new Inter1Impl1());
//
//        demo1.show1(new Inter1Impl2());

        demo1.show1(new Inter1() {
            @Override
            public void fun1() {
                System.out.println("李刚今天下午没有睡觉...");
            }
        });

        demo1.show1(new Inter1() {
            @Override
            public void fun1() {
                System.out.println("江川今天下午睡觉了...");
            }
        });

        //扩展知识,今天不细说,最后一天的时候细说
//        demo1.show1(new Inter1() {
//            @Override
//            public void fun1() {
//                System.out.println("数加666");
//            }
//        });
        //若匿名内部类是一个接口的对象,且接口中只有一个抽象方法,就可以改写以下写法格式
//        demo1.show1(()->System.out.println("数加666")); // lambda表达式

    }
}

============================================================
abstract class Demo2{
    public abstract void fun1();
}

//class Demo2Zi extends Demo2{
//    @Override
//    public void fun1() {
//        System.out.println("好好学习,天天向上!");
//    }
//}


class Student2{
    public void show1(Demo2 demo2){ //Demo2 demo2 = new Demo2Zi()
        demo2.fun1();
    }
}

public class NiMingClassDemo2 {
    public static void main(String[] args) {
        Student2 student2 = new Student2();
//        student2.show1(new Demo2Zi());
        student2.show1(new Demo2() {
            @Override
            public void fun1() {
                System.out.println("好好学习,天天向上!");
            }
        });
    }
}
============================================================
    
interface Inter2{
    void fun1();
}

//class Inter2Impl implements Inter2{
//    @Override
//    public void fun1() {
//        System.out.println("好好学习,天天向上!");
//    }
//}

class Demo3{
    public Inter2 show1(){
        return new Inter2() {
            @Override
            public void fun1() {
                System.out.println("好好学习,天天向上!");
            }
        };
    }
}
public class NiMingClassDemo3 {
    public static void main(String[] args) {

    }
}