389-411代码块练习/单例设计模式/final/抽象类/接口

一、代码块的练习

package com.alice.day.day513;

import com.use.Test;

/**
 * 第一题
 * 下面的代码输出什么?
 */
public class CodeBlockExercise01 {
    // 第二题的内容
    Sample sam1 = new Sample("sam1成员初始化");  // 该类静态完成之后普通
    // 这里会先加载类的信息,所以sam1是普通成员属性,不是静态的不会被先加载
    static Sample sam = new Sample("静态成员sam初始化"); // 是静态先加载
    // 因为这里new Sample创建实例对象会加载类的信息,所以又去跑去加载类信息,然后普通,然后构造
    static {
        System.out.println("static块执行"); // 回到该类执行剩下的还没有完成的静态代码块
        if (sam==null) System.out.println("sam is nul");  // 已经有地址不会执行,到此为止
        // 静态完成,然后是普通
    }

    CodeBlockExercise01(){
        System.out.println("CodeBlockExercise01默认构造函数被调用");  // 最后构造
    }
    public static void main(String[] args) {
        // 1
//        System.out.println("total = " + Person.total);  // total =
//        // 分析,这里Person.total调用的是类的东西,肯定要加载类的信息,而加载类的信息是按照
//        // 先静态,优先级相同,谁在前面谁先执行,所以先total,然后静态代码块,而这里的total
//        // 没有输出,所以这里total先被初始化为0然后被赋值为100,然后打印in static block!
//        // 然后回到调用处Person.total 打印total = 100;
//        // 即:
//        // in static block!
//        // total = 100
//        System.out.println("total = " + Person.total);
//        // 第二次因为类的信息只会加载一次,所以这里静态代码块就不执行了,直接打印:
//        // total = 100

        // 2、
        CodeBlockExercise01 a = new CodeBlockExercise01();
        // 其实主函数什么都不写还是会有输出的,因为Java帮我们调用了静态main,导致类信息被加载
        // 这里创建对象调用无参构造方法
        /*
            输出:
                Sample
                你好
                静态成员sam初始化
                static块执行
                你好
                sam1成员初始化
                CodeBlockExercise01默认构造函数被调用
         */
    }
}

class Person {
    public static int total;
    static {
        total = 100;
        System.out.println("in static block!");
    }
}

/**
 * 第二题,下面的代码输出什么
 */
class Sample {
    static {
        System.out.println("Sample");  // 先被执行
        // 第一个输出Sample
    }
    {  // 创建对象导致普通的代码块又被执行
        System.out.println("你好"); // 然后普通
    }
    Sample(String s) {  // 最后构造器
        System.out.println(s);  // 最后构造
    }
    Sample() {
        System.out.println("Sample默认构造函数被调用");
    }
}

二、单例设计模式(入门)

package com.alice.day.day513;


/*
    单例设计模式
    什么是设计模式
    1、静态方法和属性的经典使用
    2、设计模式是在大量的实践中总结和理论化之后优选的代码结构,编程风格,以及解决
    问题的思考方式。设计模式就像是经典的棋谱,不同的棋局,我们需要使用不同的棋谱
    免得再次去思考和摸索。
    这里考虑设计模式的时候会使用到大量和静态相关的内容。

    什么是单例模式
    1、所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,
    并且该对象只能提供一个取得其对象实例的方法
    2、单例模式有两种方式:
        第一种是 饿汉式
        第二种是 懒汉式

    实现步骤:
    1、构造器私有化,防止直接new一个对象,但是这样我们不能有女朋友
    2、类的内部创建对象,可以有女朋友但保证只有一个,这个时候就需要使用到static静态属性
    3、向外暴露一个静态的公共方法,getInstance给一个实例,让我们得到女朋友
    4、代码实现
 */
public class SingletonDesignPattern02 {
    public static void main(String[] args) {
        GirlFriend instance = GirlFriend.getInstance();
        System.out.println(instance);

        // 获取我们可以一直获取,但是其实都是同一个对象
        GirlFriend instance1 = GirlFriend.getInstance();
        System.out.println(instance1);

        System.out.println(instance == instance1);

    }
}

// 有一个类,GirlFriend
// 只能有一个女朋友
// 像下面这种就不是一个单例设计模式
//GirlFriend xh = new GirlFriend("小红");
//GirlFriend xb = new GirlFriend("小白");
//class GirlFriend {
//    private String name;
//
//    public GirlFriend(String name) {
//        this.name = name;
//    }
//}

/**
 * 为什么叫做饿汉式,
 * 创建一个public static int n1 = 100;
 * main中只写了        System.out.println(GirlFriend.n1);
 * 这个时候gf对象已经创建好了
 */
class GirlFriend {
    public static int n1 = 100;
    private String name;
    private static GirlFriend gf = new GirlFriend("小红红"); // 为了能够在静态方法中返回需要修饰为静态
    // 即没有创建对象就可以使用这个方法
    private GirlFriend(String name) {
        this.name = name;
    }
    public static GirlFriend getInstance() {
        return gf;
    }

    @Override
    public String toString() {
        return "GirlFriend{" +
                "name='" + name + '\'' +
                '}';
    }
}
package com.alice.day.day513;

public class SingletonDesignPattern03 {
    public static void main(String[] args) {
        System.out.println(Cat.n1); // 虽然加载了类,但是没有new
        Cat cat = Cat.getInstance();
        System.out.println(cat);

        Cat cat1 = Cat.getInstance();
        System.out.println(cat1);

        System.out.println(cat == cat1);  // 将上一次对象返回
    }
}

/*
    饿汉式式没有使用也给你创建实例
    懒汉式是使用的时候才给你创建实例

    所以饿汉式的对象一般都是重量级的对象,因为创建了不使用就会造成创建了对象但是没有时候的空间浪费
 */

class Cat {
    // 我们希望在程序运行过程中只能创建一个Cat对象
    // 也使用单例设计模式
    private String name;
    public static int n1 = 999;
    // 仍然将构造器私有化防止new
    private static Cat cat;

    public Cat(String name) {
        this.name = name;
    }

    public static Cat getInstance() {
        if (cat == null) { //如果还没有创建cat对象
            cat = new Cat("小可爱");
        }
        return cat;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                '}';
    }
}

/*
    饿汉式和懒汉式的对比
    1、二者最主要的区别式创建对象的时机式不同的:饿汉式式在类加载就创建了对象实例,而懒汉式是在使用的时候才创建
    2、饿汉式不存在线程安全问题,懒汉式存在线程安全性问题。(后面学习线程之后,会完善)
        public static Cat getInstance() {
        if (cat == null) { //如果还没有创建cat对象
            cat = new Cat("小可爱");
        }
        return cat;
    }
    比如三个线程进来执行,这个这个瞬间都判断没有创建对象,这样三个线程都会创建对象
    只不过保留的是最后那个线程创建的对象而已

    3、饿汉式存在浪费资源的可能,因为如果程序员没有使用对象实例,那么懒汉式创建的对象就浪费了,懒汉式是使用
    才创建,就不存在这个问题。
    4、在JavaSE标准类中,java.lang.Runtime就是经典的单例模式

    小结:
    1、单例模式的两种实现方式,第一种是饿汉式,第二种是懒汉式
    2、饿汉式,创建了,式在类加载的时候就创建,可能存在资源浪费问题
    3、懒汉式的问题:线程安全问题,后面学习了线程之后再进行完善。
    4、要求可以独立写出单例模式。
 */

三、final

package com.alice.day.day513;

public class FinalCode04 {
    public static void main(String[] args) {
//        E e = new E();
//        System.out.println(e.TAX_RATE);
//        e.TAX_RATE = 10;  // 不能被修改
    }
}

/*
    final中文意思是:最后的,最终的。
    final可以修饰类、属性,方法和局部变量。
    在某些情况下,程序员可能有以下需求,就会使用到final:
    1、当不希望类被继承的时候可以使用final修饰
    2、当不希望父类的某个方法被子类覆盖/重写Override的时候,可以使用final关键字
    3、当不希望类的某个属性的值被修改,可以使用final修饰。
    4、当不希望某个局部变量被修改,可以使用final修饰。
 */

///**
// * 不希望别的类继承,因为这个类很重要
// */
////final class A{}
////class B extends A{}如果是这样这里就会报错

//class C {
//    // 如果我们要求hi方法不能被子类重写
//    // 可以使用final修饰 hi方法
//    public final void hi() {
//    }
//}
//
//class D extends C {
//
//    public void hi() {
//        System.out.println("重写了C类的hi方法...");
//    }
//}

///**
// * 不能被修改
// */
//class E {
//    public final double TAX_RATE = 0.08;
//
//    private void ee() {
//        TAX_RATE = 10;
//    }
//}

///**
// * 修饰局部不能修改
// */
//class F {
//    public void ff(final int VALUE_F) {
//        VALUE_F = 100;
//        final double FF = 10.0;
//        FF = 100;
//    }
//}
package com.alice.day.day513;

public class FinalDetail05 {
    public static void main(String[] args) {
        C c = new C();
        new EE().cal();
    }
}

/*
    final使用注意事项和细节讨论
    FinalDetail
    1、final修饰的属性又叫做常量,一般使用XX_XX_XX来命名
    2、final修饰的属性在定义的时候,必须赋初值,并且以后不能再修改,赋值可以加在如下的位置之一,选择一个位置赋初值
    即可
        定义的时候,比如public final double TAX_RATE = 0.08;
        在构造器中
        在代码块中
    3、如果final修饰的属性是静态的,则初始化的位置只能是
        定义的时候,在静态代码块中不能在构造器中赋值
    4、final类不能继承,但是可以实例化对象。
    5、如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。
 */

/**
 * 1、final修饰的属性可以在定义的时候赋值
 * 2、可以在代码块中赋值
 * 3、可以在构造器中赋值
 * final必须赋值
 * 赋值后不能修改
 */
class AA{
    public final double TAX_RATE = 0.08;  // 在定义的时候赋值
    public final double TAX_RATE2;
    public final double TAX_RATE3;

    public AA() {  // 在构造器中赋值
        TAX_RATE2 = 1.1;
    }

    {
        // 在代码块中赋值
        TAX_RATE3 = 8.8;
    }
}
/**
 *如果final修饰的属性是静态的,那么可以在以下三个地方进行赋值
 * 1、定义的时候
 * 2、静态代码块中
 * 不能在构造器中赋值
 * 为什么不能在构造器中赋值呢?因为构造器是创建对象的,而静态的属性是在类加载的时候
 * 所以不行
 */
class BB {
    public static final double TAX_RATE = 99.9;  // 在定义的时候赋值
    public static final double TAX_RATE2;
//    public static final double TAX_RATE3;
    public BB() {
//        TAX_RATE3 = 8.8;  // 不能在构造器中赋值
    }
    static {
        TAX_RATE2 = 3.3;  // 在静态代码块中赋值
    }
}

// final类不能继承,但是可以实例化对象的
final class C{}

class DD {
    public final void cal() {
        System.out.println("cal()方法");
    }
}
class EE extends DD{
    // 可以继承下来使用,不能重写但是可以使用
}

package com.alice.day.day513;

public class FinalDetail06 {
    public static void main(String[] args) {
        System.out.println(BBB.num);
    }
}

/*
    final的细节第二部分,
    6、一般来说,如果一个类已经是final类了,就没有必要再将方法修饰称final方法的必要。
    比如写了一个类,这个类是一个final类,然后一个方法是final方法,这个类是不能被继承,所以方法不能够被继承
    所以final修饰方法表示这个方法不能继承,这两个功能其实就是一个画蛇添足了。所以方法就不用添加final了.
    final class AAA{
        public final void cry() {}
    },不过加上去还是可以.
    7、final不能修饰构造方法
    final 方法:防止子类重写。构造器:子类根本不能继承或重写父类构造器,所以 final 修饰构造器是多余的。
    8、final和static往往搭配使用,效率更高,底层编译器做了优化处理。
    比如现在类里面有一个静态的属性以及静态的代码块,如果我们调用这个类的这个属性的时候肯定会导致类的加载,
    但是如果我们在这个静态属性加上一个final就不会导致类的加载,也就是说这个静态代码块不会被执行了.
    9、包装类(Integer,Double,Float,Boolean等都是final),String也是final类。
    也就是说这些类都不能被继承.
 */
class BBB {
    public final static int num = 10000;
    static {
        System.out.println("BBB 静态代码块被执行");
    }
}

package com.alice.day.day513;

public class FinalExercise07 {
    public static void main(String[] args) {
        Circle1.s(33);
        Circle2.s(33);
        new Circle3().s(33);
    }
}

/**
 * 第一题,请编写一个程序,能够计算圆形的面积,要求圆周率为3.14,赋值的位置有三个
 * 三个方式都写出来.
 * 写一个类,类中有半径普通
 * final属性pi
 * 一个构造器,在构造器中赋值,或者是定义的时候赋值,还有一种是在代码块中赋值
 */
// 第一个位置
class Circle1 {
    private static final double PI = 3.14;
    public static void s(double r) {
        System.out.println(PI * r * r);
    }
}

// 第二个位置
class Circle2 {
    private static final double PI;
    static {
        PI = 3.14;
    }
    public static void s(double r) {
        System.out.println(PI * r * r);
    }
}

// 第三个位置
class Circle3 {
    private final double PI;
    public Circle3 () {
        PI = 3.14;
    }
    public void s(double r) {
        System.out.println(PI * r * r);
    }
}

// 第二道题,程序阅读题
class Something {
    public int addOne(final int x) {  // 可以的
//        ++x;  // 这里尝试对一个常量进行修饰,错误
        return x + 1;  // 这是是对返回结果进行处理没有关系
    }
}

四、抽象类

package com.alice.day.day513;

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

    }
}

/*
    根据一个问题引出抽象类
    有一个动物类,
    属性name,age
    构造器
    吃东西行为,对于动物来说我们知道eat方法一般是子类重写,因为不同动物吃的什么不一样
    即:父类方法的不确定性
    当父类的某些方法,需要生命的时候,但是又不能确定如何实现的时候,可以将其声明为抽象方法,这个类就是抽象类了
 */

abstract class Animal {
    private String name;
    private int age;
    public Animal(String name) {
        this.name = name;
    }
//    public void eat() {  // 这里实现了但是没有什么意义,这里就出现了父类方法不确定性问题
//        // 考虑将该方法设计为抽象方法,abstract是抽象的意思,需要添加这个关键字,
//        // 所谓抽象方法就是没有实现的方法,什么是没有实现呢?就是指的是没有方法体.
//        System.out.println("这是一个动物类,但是不知道吃什么");
//    }
    public abstract void eat();  // 一旦一个类中存在了抽象方法之后,这个类就需要使用这个关键字声明为抽象类.
    // 一般来说,抽象类会被继承,由该类的子类来实现抽象方法.
}
package com.alice.day.day513;


/*
    抽象类的介绍
    1\使用abstract关键字来修饰一个类的时候,这个类就叫做抽象类.
    访问修饰符 abstract 类名 {}
    2\使用abstract关键字来修饰一个方法的时候,这个方法就是抽象方法.
    访问修饰符 abstract 返回类型 方法名(参数列表);  // 没有方法体
    3\抽象类的价值更多的是在于设计,是设计者设计好之后,让子类继承来实现.
    4\抽象类是考官比较爱问的知识点,在框架和设计模式使用比较多.

    抽象类细节比较多一共又八个点
    抽象类使用的注意事项和细节说明
    1\抽象类不能被实例化
    2\抽象类不一定要包含abstract方法,也就是说,抽象类中可以没有abstract方法
    3\一旦类中包含了abstract方法,则这个类必须声明为abstract
    4\abstract只能修饰类和方法,不能修饰属性和其他的.
 */
//// 1\抽象类不能被实例化
//public class AbstractDetail09 {
//    public static void main(String[] args) {
//        new AAA();  // 抽象类不能被实例化
//    }
//}
//abstract class AAA{}

//// 2\抽象类可以没有抽象方法
//abstract class AAA{
//    public void hi(){
//        System.out.println("你也好");
//    }
//}  // 抽象类可以没有抽象方法,有实现方法都可以

// 3\一旦类包含了abstract方法,则这个类也必须声明为抽象的.
//abstract class AAA{
//    abstract public void hi();
//}

// abstract只能修饰类和方法,不能修饰属性和其他的.


package com.alice.day.day513;

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

    }
}

/*
    抽象类可以又任意的成员,因为抽象类还是类,比如:非抽象方法,构造器,静态属性等等,

    抽象方法不能有主体,即不能实现

    如果一个类继承了抽象类,则它必须实现抽象类中的所有抽象方法,除非它自己也声明为abstract类.

    抽象方法不能使用private,final和static来修饰,因为这些关键字都是和重写相违背的.
 */
// 1\
abstract class D{
    public int n1 = 10;
    public static String name = "你好呀";
    public void h1() {
        System.out.println("h1");
    }
    public abstract void hello();
    public static void ok() {
        System.out.println("ok");
    }
}

// 3
abstract class E{
    public abstract void hi();
}

abstract class F extends E {
    // 除非自己也是一个抽象类
}

class G extends E {
    // 或者自己实现
    @Override
    public void hi() {
        // 实现就是要有方法体,具体怎么实现不关心.
    }
}

package com.alice.day.day513.test20;

//public class AbstractExercise01 {
//    public static void main(String[] args) {
////        A a = new B();
////        a.test2();  // 我们知道动态绑定调用的应该是运行时类型的
////
////        a.test3();  // 这里动态绑定机制,调用的是B类重写的方法
////
////        a.test4();  // 通过动态绑定机制调用了子类的方法
//    }
//}

/*
    课堂练习
    AbstractExercise01
    第一题:
        思考 abstract final class A{}能编译通过吗?为什么?
        首先final修饰的类不能被继承,而abstract修饰的类需要被继承实现,所以不能通过
    第二题:
        思考 abstract public static void test2(); 能编译通过吗?为什么?
        abstract没有具体的实现,需要子类继承之后重写实现
        通过动态绑定机制调用子类的方法,体现了多态。
        static是和类绑定的,不和实例绑定,可以通过类名直接调用
        不能被重写,子类可以声明同名的静态方法,这里隐藏了而非重写,不能体现多态。
        即出现了矛盾,一个可以重写,一个不能重写,一个体现了多态,一个3不体现多态,所以不行。
        即abstract动态绑定机制,一个static是编译绑定
    第三题:
        思考 abstract private void test3(); 能通过编译吗?为什么,
        这里因为abstract定义的是一个抽象方法,需要被子类继承重写实现,而这里却是
        一个private的访问权限,说明子类不能访问这个方法,所以这里产生了语法错误不能通过编译。


 */

// 第一题
//abstract final class A{}  // 产生语法错误不能编译通过

//// 第二题
//abstract  class A {
//    public static void test2(){
//        System.out.println("你好,我是A");
//    }
//
//    public void test3() {
//        System.out.println("你好,我是A中的普通成员方法,我可以重写");
//    }
//
//    abstract public void test4();
//}
//class B extends A {
//    public static void test2(){  // 这样看起来是重写了,但是其实没有实现重写
//        System.out.println("你好,我是B");
//    }
//
//    @Override
//    public void test3() {
//        System.out.println("你好,我是B类中的普通成员方法,我重写了A的普通成员方法");
//    }
//
//    @Override
//    public void test4() {
//        System.out.println("这里是B,重写了A的test4");
//    }
//}
public class AbstractExercise01 {
    public static void main(String[] args) {
        Manager jack = new Manager("jack", "999", 50000, 8000);
        jack.work();
        CommonEmployee tom = new CommonEmployee("tom", "888", 20000);
        tom.work();

    }
}
/*
    第四题:
        编写一个Employee类,声明为抽象类,包含如下三个属性:name,id,salary。
        提供必要的构造器和抽象方法:work()。对于Manager类来说,它既是员工,还是
        具有奖金的bonus属性,请使用继承的思想,设计CommonEmployee类和Manager类
        ,要求类中提供必要的方法进行属性访问,实现work(),提示
        “经理/普通员工 名字 工作中。。。”
 */
abstract class Employee {
    private String name;
    private String id;
    private double salary;

    public Employee(String name, String id, double salary) {
        this.name = name;
        this.id = id;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    abstract public void work();
}
class Manager extends Employee{
    private double bonus;

    public Manager(String name, String id, double salary, double bonus) {
        super(name, id, salary);
        this.bonus = bonus;
    }

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

    @Override
    public void work() {
        System.out.println("经理" + getName() + "工作中");  // 经理/普通员工 名字 工作中。。。
    }
}

class CommonEmployee extends Employee {
    public CommonEmployee(String name, String id, double salary) {
        super(name, id, salary);
    }

    @Override
    public void work() {
        System.out.println("普通员工" + getName() + "工作中");
    }
}
package com.alice.day.day513.test20;



/*
    抽象类在什么地方会使用到呢?
    在模板设计模式这里会使用到,这是一个非常经典的应用场景
    也就是设计模式里面的模板模式
    现在给出一个需求:
        1、有多个类,需要完成不同的任务job
        2、要求能够得到各自完成任务的时间
        3、请编程实现 TestTemplate.java
 */
public class AbstractBestPractices02 {
    public static void main(String[] args) {
        A a = new A();
        a.calculateTime();
        B b = new B();
        b.calculateTime();
    }
}
class A extends Template{
    public void job() {
        long num = 0;
        for (int i = 1; i < 8000000; i++) {  // 可以通过10000.for回车自动生成
            num += i;
        }
    }
}  // 在A中写一个job方法计算1加到8000000的总和
class B extends Template{
    public void job() {
        long num = 0;
        for (int i = 1; i < 800000; i++) {  // 可以通过10000.for回车自动生成
            num *= i;
        }
    }
}  // 我们需要统计一个*的方式800000
//AA执行时间是:5
//BB执行时间是:2
/*
    发现我们的类中有了重复的代码,也就是我们计算时间的代码
    如果现在还有一个另外的工作,统计1+10等的工作
    这样代码复用性非常差,我们需要将公用的代码提取出来
    将统计开始时间,结束时间,以及打印总执行时间的部分,拿出来
    而中间执行的部分我们直接调用即可。
    public void calculateTime() {
        // 得到开始的时间
        long start = System.currentTimeMillis();
        job();
        // 得到的结束时间
        long end = System.currentTimeMillis();
        System.out.println("AA执行时间是:" + (end-start));
    }
    public void job() {
        long num = 0;
        for (int i = 1; i < 8000000; i++) {  // 可以通过10000.for回车自动生成
            num += i;
        }
    }
    同理B中我们也可以这样做
    这个时候我们A B中都有这样统计时间的一个方法
    这个地方就方便了,将共有的部分提取出来,产生了新的方法
    这个抽取出来的方法,这两个是一样的,以后我们都这样做吗
    这个时候就需要提出观点,抽象一个父类,父类中这个地方就是一个模板类
    写成一个抽象的方法,将这些计算时间的方法抽象到父类的方法中
    形成一个模板设计模式。
 */
abstract class Template {  // 抽象类,模板设计模式
    public abstract void job();  // 抽象方法
    public void calculateTime() {  // 实现了的方法,调用job方法即可
        // 得到开始的时间
        long start = System.currentTimeMillis();
        job();
        // 得到的结束时间
        long end = System.currentTimeMillis();
        System.out.println("执行时间是:" + (end-start));
    }
}

五、接口

package com.alice.day.day513.test20.interface03_;

public class Interface03 {
}

/*
    U盘,摄像机,手机,我们日常生活中可能经常插入到电脑上面使用
    这里的USB就是现实生活中的一个接口
    USB,插槽就是现实生活中的接口
    你可以将手机,相机,U盘都插在USB插槽上,不用担心那个插槽是专门插哪个的,原因是做usb插槽的场景和做各种设备的厂家
    都遵循了统一的规定包括尺寸,排线等等。
    我们使用编程来模拟这样的一个情况
 */
 
package com.alice.day.day513.test20.interface03_;

public class Interface_ {
    public static void main(String[] args) {
        Camera camera = new Camera();
        Phone phone = new Phone();
        Computer computer = new Computer();
        computer.work(phone);
        computer.work(camera);
    }
}
package com.alice.day.day513.test20.interface03_;

public class Phone implements UsbInterface{
    // Phone类实现UsbInterface
    // Phone需要实现UsbInterface的接口

    @Override
    public void start() {
        System.out.println("手机开始工作...");
    }

    @Override
    public void stop() {
        System.out.println("手机停止工作...");
    }
}
package com.alice.day.day513.test20.interface03_;

public class Camera implements UsbInterface{  // 机器实现接口
    @Override
    public void start() {
        System.out.println("相机开始工作...");
    }

    @Override
    public void stop() {
        System.out.println("相机停止工作...");
    }
}
package com.alice.day.day513.test20.interface03_;

public class Computer {
    // 编写一个方法,计算机工作
    public void work(UsbInterface usbInterface) {
        usbInterface.start();
        usbInterface.stop();
    }
}
package com.alice.day.day513.test20.interface03_;

public interface UsbInterface { // 接口,比较像现实生活中的规范
    // 规定接口的相关方法
    public void start();
    public void stop();
}

package com.alice.day.day513.test20.interface04_;

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

    }
}

/*
    接口的基本介绍:
        接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况将这些方法写出来。
        语法:
            interface 接口名 {
            // 属性
            // 方法
            }
            class 类名 implements 接口 {
            自己属性;
            自己方法;
            必须实现的接口的抽象方法
            }
            在接口中抽象方法可以省略abstract关键字
           小结:
            在JDK7.0之前,接口里面的所有方法都没有方法体,
            JDK8.0之后接口类可以有静态方法,默认方法,也就是说接口中可以又方法的具体实现
 */
interface AInterface {
    // 写属性
    public int n1 = 10;
    // 写方法
    // 在接口中,抽象方法,可以省略abstract关键字
    public void hi();
    // j8后可以又默认实现方法,但是需要使用default关键字修饰
    // 另外还可以又静态方法
    default public void ok() {
        System.out.println("ok......");
    }
    public static void cry() {
        System.out.println("cry ........");
    }

}

/**
 * 1、如果是一个类 implements实现接口
 * 2、需要将该接口的所有抽象方法实现
 */
class A implements AInterface {
    @Override
    public void hi() {
        System.out.println("hi()......");
    }
}
package com.alice.day.day513.test20.interface05_;

public class Interface05 {
    public static void main(String[] args) {
        MysqlDB mysqlDB = new MysqlDB();
        t(mysqlDB);
        OracleDB oracleDB = new OracleDB();
        t(oracleDB);
    }

    public static void t(DBInterface db) {
        db.connect();
        db.close();
    }
}

/*
    接口对于初学者来说,理解接口的概念不算太难,难的是不知道什么时候使用接口,下面我们通过几个例子来举例说明
    1、现在要制造战斗机,武装直升机,专家只需要将飞机所需要的功能/规格定下来即可,然后让别人具体实现就好。
    2、现在有一个项目经理,管理三个程序员,功能开发一个软件,为了控制和管理软件,项目经理可以定义一些接口
    然后由程序员具体实现
    直接写类不就行了,为什么还需要写接口呢?
    但是谁来规范这些写出来的类呢?没有规范控制软件的质量和规范,不能控制功能,接口还涉及到一个统一调用的问题。
    比如现在我们有一个实际的要求,要求三个程序员编写三个类分别完成对mysql,oracle,DB2数据库的链接。
    这三个程序员分别写的方法名称比如:
    A程序员链接MySQL
    f1() f2()
    B程序员链接Oracle
    con()
    close()
    C程序员链接DB2
    connect()
    shutdown()
    每个人编写的名字都不同,如果这个时候使用接口来规范这些名字就会非常方便。


 */

interface DBInterface {  // 项目经理
    public void connect(); // 链接方法
    public void close();  // 关闭链接
}

class MysqlDB implements DBInterface {
    // A程序员实现接口

    @Override
    public void connect() {
        System.out.println("链接mysql");
    }

    @Override
    public void close() {
        System.out.println("关闭mysql");
    }
}

// B程序员链接Oracle
class OracleDB implements DBInterface {
    @Override
    public void connect() {
        System.out.println("连接oracle");
    }

    @Override
    public void close() {
        System.out.println("关闭oracle");
    }
}
package com.alice.day.day513.test20.interface06_;

//public class Interface06 {
//    public static void main(String[] args) {
//        new IA();  // 1、接口不能被实例化
//    }
//}
//interface IA {}
/*
    关于接口的注意事项和细节
    1、接口不能被实例化
    2、接口中的所有方法是public方法,接口中的抽象方法,可以不用abstract修饰
    3、一个普通类实现接口,就必须将该接口的所有方法都实现。
    4、抽象类实现接口,可以不用实现接口的方法。
 */
// 2、接口中所有的方法都是public方法,接口中抽象方法,可以不使用abstract修饰
// 设置成其他的访问修饰符不可以
//interface IA {
//    void say();
//    // 这里默认都是public的,默认也是abstract
//    // public abstract void say();
//}

//// 3、一个普通类实现接口,就必须将该接口的所有方法都实现
//interface IA {
//    void say();
//    void hi();
//}
//class Cat implements IA {  // 出现红色波浪线的时候可以直接Alt + Enter快速生成重写代码
//
//    @Override
//    public void say() {
//
//    }
//
//    @Override
//    public void hi() {
//
//    }
//}

// 4、抽象类可以不用实现
interface IA {
    void say();
    void hi();
}
abstract class Tiger implements IA {}
package com.alice.day.day513.test20.interface07_;



/*
    接口的细节
    5、一个类同时可以实现多个接口
    6、接口中的属性,只能是final的,而且是public static final 修饰符,比如:
    int a = 1;实际上是public static final int a = 1;(必须初始化)
    7、接口中属性的访问形式:接口名.属性名
    8、一个接口不能继承其他的类,但是可以继承多个别的接口
    9、接口的修饰符只能是public和默认,这一点和类的修饰符是一样的。
 */
//// 1、一个类可以同时实现多个接口
//interface IB {
//    void hi();
//}
//interface IC {
//    void say();
//}
//class Pig implements IB,IC {
//
//    @Override
//    public void hi() {
//
//    }
//
//    @Override
//    public void say() {
//
//    }
//}
//// 2、int n1 = 10等价于 public static final int n1 = 10;
//public class Interface07 {
//    public static void main(String[] args) {
//        System.out.println(IB.n1);  // 这里证明是static的
//        // IB.n1 = 30;  // 这里证明是final的
//        // 而要判断是public的不是默认的只需要通过javap反编译或者是在另外一个包中访问即可,如果能够访问就是public
//    }
//}
//interface IB {
//    // 接口中的属性,只能是final的,并且是public static final 修饰符
//    int n1 = 10;
//}

//// 3、接口不能继承其他的类,但是可以继承多个别的接口
//interface IB {}
//interface IC {}
//interface ID extends IB, IC {}

package com.alice.day.day513.test20.interface08_;

public class InterfaceExercise01 {
    public static void main(String[] args) {
        B b = new B();
        System.out.println(b.a);  // 通过实例对象调用继承过来的属性是可以的
        System.out.println(A.a);  // 通过A或者B这种“类名”调用也是可以的。
        System.out.println(B.a);
    }
}

interface A {  // 接口也可以理解为一个类
    int a = 23;
    // 首先这里等价于public static final int a = 23;
}
class B implements A{  // 使用类去实现接口,这个是可以的。实现也可以粗略的理解为继承
    // 语法是否正确,如果正确,输出什么?
    // 这里因为接口没有抽象方法,所以这里不需要实现。
}

package com.alice.day.day513.test20.interface09_;



/*
    接口和继承类的比较
    实现接口和继承类又什么区别呢?
    就像你继承你爹的特性,你爹的遗产
    你如果想像小鸟一样飞翔就需要制作飞行器来实现
    如果想要像小鱼一样游泳,就需要制作潜水艇来实现

    理解:实现机制是对于单继承机制的补充
 */
public class Interface09 {
    public static void main(String[] args) {
        LittleMonkey wuKong = new LittleMonkey("悟空");
        wuKong.climbing();
        wuKong.swimming();
        wuKong.flying();
    }
}
// 创建一个小猴子类
class LittleMonkey extends Monkey implements Fishable,Birdable{
    public LittleMonkey(String name) {
        super(name);
    }

    @Override
    public void swimming() {
        System.out.println(getName() + " 通过学习,可以像鱼儿一样游泳。");
    }


    @Override
    public void flying() {
        System.out.println(getName() + " 通过学习,可以像小鸟一样飞翔。");
    }
}
// 创建一个老猴子类
class Monkey {
    private String name;
    public Monkey(String name) {
        this.name = name;
    }
    public void climbing() {
        System.out.println(name + " 会爬树...");
    }

    public String getName() {
        return name;
    }
}
// 创建一个鱼的接口(功能)
interface Fishable {
    void swimming();
}
interface Birdable {
    void flying();  // 还可以像小鸟一样飞翔
}
// 小结:
// 当子类继承了父类,就自动拥有了父类的功能
// 如果子类需要扩展功能,可以通过实现接口的方式来扩展,
// 实现接口是对Java单继承机制的一种补充。

/*
    总结:
        实现接口和继承类
        接口和继承解决的问题不同
        继承的价值主要在于:解决代码的复用性和可维护性
        接口的价值在于:设计,设计好各种的规范(方法),让其他类去实现这些方法。
        接口比继承更加灵活,接口比继承更加灵活,继承是满足is - a的关系,而接口只需满足lik-a的关系。
        接口在一定程度上实现代码的解耦(接口的规范性+动态绑定)
 */
package com.alice.day.day513.test20.interface10_;

//public class interface10 {
//    // 接口的多态体现
//    // 接口类的变量if01可以指向实现了IF接口的对象实例
//    public static void main(String[] args) {
//        IF if01 = new Monster();
//        if01 = new Car();
//    }
//}
//interface IF {}
//class Monster implements IF {}
//class Car implements IF {}

/*
    接口
    接口的多态特性
    1、多态参数
    在前面的时候使用Usb接口案例,Usb usb,既可以接收手机对象,又可以接收相机对象,这就体现了接口多态
    (接口引用可以指向实现了接口的类的对象)
    2、多态数组
    演示一个案例:给Usb数组中,存放Person和相机对象,Phone类中还有一个特定的方法call
    ,请遍历Usb数组,如果是Phone对象,除了调用Usb接口定义的方法之外,还需要调用Phone特有的方法call。
    3、接口存在多态传递现象。InterfacePolyPass.java
 */

//public class interface10 {
//
//    public static void main(String[] args) {
            // 继承体现的多态
            // 父类类型的变量a可以指向继承AAA的子类的对象实例
//        AAA a = new BBB();
//        a = new CCC();
//    }
//}
//class AAA {}
//class BBB extends AAA {}
//class CCC extends AAA {}

public class interface10 {

    public static void main(String[] args) {
        // 多态数组->接口类型数组
        Usb[] usbs = new Usb[2];
        usbs[0] = new Phone_();
        usbs[1] = new Camera_();

        for (int i = 0; i < usbs.length; i++) {
            usbs[i].work();  // 动态绑定机制
            // 如果需要一个执行特定的方法需要进行一个类型的判断
            if (usbs[i] instanceof Phone_) {
                // 判断运行类型是否为Phone_
                ((Phone_) usbs[i]).call(); // 将编译类也变成这个就行了
            }
        }
    }
}
interface Usb{
    void work();
}
class Phone_ implements Usb {
    public void call() {
        System.out.println("手机可以打电话...");
    }

    @Override
    public void work() {
        System.out.println("手机工作中...");
    }
}
class Camera_ implements Usb {
    @Override
    public void work() {
        System.out.println("相机工作中...");
    }
}
package com.alice.day.day513.test20.interface11_;


public class Interface11 {
    public static void main(String[] args) {
        // 接口类型的变量可以指向,实现了该接口的类的对象实例
        IG ig = new Teacher();
        IH ih = new Teacher();  // 所以IH也能够指向,因为接口IG继承了IH
    }
}
interface IH {
    void hi();
}
interface IG extends IH {}  // 又因为IG继承了IH,所以也就相当于Teacher实现了IH接口
class Teacher implements IG {  // 也要实现IH,这就是多态传递现象

    @Override
    public void hi() {
        System.out.println("实现IH的hi方法");
    }
}
/*
    接口的多态特性
    第三个:接口存在多态传递现象,
 */

package com.alice.day.day513.test20.interface12_;

public class Interface12 {
    public static void main(String[] args) {
        new C().pX();
    }
}

interface A{
    int x = 0;  // 这里相当于是public static final int x = 0;
}
class B {
    int x = 1;
}
class C extends B implements A {
    public void pX() {
//        System.out.println(x);  // 这里继承的B类中又一个x,要实现的接口A中也有一个x,不知道是哪一个
        // 所以就需要指明使用哪一个的x
        // 如果是访问接口的x,就使用A.x
        // 如果是访问父类的x,就是用super.x
        System.out.println(A.x + " " + super.x);
    }
}
// 看看有没有错误,有错误就修改,改好之后看看输出。

/*
    回顾:
        成员变量,成员方法,构造方法,包,继承,接口,代码块等等
        下一个阶段开始学习内部类,内部类是我们程序员的一个分水岭
        内部类学好,对于面向对象的认识将再上一个台阶。
 */
posted @ 2025-05-13 22:00  请叫我虾  阅读(28)  评论(0)    收藏  举报