10章 面向对象中级

类变量

基本介绍

什么是类变量?
类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的兑现过去访问它时,渠道的都是相同的值;同样,任何一个该类的对象都可以修改它,修改的也是同一个变量。
如何定义
访问修饰符 static 数据类型 变量名;【推荐这种】
static 访问修饰符 数据类型 变量名;

package com.chapter10.static_;

/**
 * @author: li
 * @version: 1.0
 */
public class ChildGame {
    public static void main(String[] args) {
        int count = 0;
        Child sun1 = new Child("sun1");
        sun1.join();
        count++;
        Child.count1++;
        //加入一个小孩 count加一次  未使用类变量
        Child sun2 = new Child("sun2");
        Child.count1++;
        Child sun3 = new Child("sun3");
        Child.count1++;
        System.out.printf("共有%d个小孩计入游戏",Child.count1);


    }
}
class Child {
    private String namel;
    //定义一个变量count,是一个类变量(静态变量)static 静态
    //该变量最大的特点就是会被child类的所有对象实例共享
    public static int count1 = 0;
    //构造器
    public Child(String namel) {
        this.namel = namel;
    }
    public void join(){
        System.out.println(this.namel + "加入了游戏");
    }
}

访问类变量
类名.列变量名 或者 对象名。类变量名
静态变量的访问修饰符的访问权限和范围 和 普通属性是一样的

package com.chapter10.static_;

/**
 * @author: li
 * @version: 1.0
 */
public class VisitStatic {
    public static void main(String[] args) {
        //类名.类变量名
        //注意:类变量是随着类加载而船舰,搜易 即使没有创建对象实例也可以访问
        System.out.println(A.i);
    }
}
class A{
    //类变量
    //类变量的访问必须遵守相关的访问权限
    public static int i = 10;
    //普通属性/普通成员变量/非静态属性/非静态成员变量/实例属性*****
    private int q = 100;
}

类变量使用注意事项和细节讨论

  1. 什么时候需要使用类变量
    当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量);比如:定义学生类,统计所有学生共交多少学费
  2. 类变量与实例变量(普通属性)的区别
    类变量是该类的所有对象共享的,而实例变量是每个对象独享的
  3. 加上static称为类变量或静态变量,否则称为实例变量
  4. 类变量可以通过 类名.类变量名 或者 对象名.类变量名 进行访问 推荐前一种形式,但是要注意访问修饰符的权限问题
  5. 实例变量不能通过类名.类变量名的形式访问
  6. 类变量在类加载的时候就初始化了,也就是说,即使没有创建对象,只要类加载了,就可以使用类变量了
  7. 类变量的生命周期随着类的加载而开始,随着类的消亡而销毁

类方法

基本介绍

类方法 也叫静态方法
形式:
访问修饰符 static 数据返回类型 方法名(){} 【推荐】
static 访问修饰符 数据返回类型 方法名(){}
类方法的调用
类名.类方法 || 对象名.类方法名
类方法应用实例

package com.chapter10.static_;

/**
 * @author: li
 * @version: 1.0
 */
public class StaticMethod {
    public static void main(String[] args) {
        Stu zhangsan = new Stu("张三");
        zhangsan.payFee(300);
        Stu.payFee(100);
        System.out.println(Stu.totalFee());
    }
}
class Stu{
    //普通成员变量
    private String name;
    //定义一个静态变量,来积累学生的学费
    private static double fee = 0;

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

    //当方法有static修饰后,就是一个静态的芳芳
    //静态方法就可以访问静态属性/变量
    public static void payFee(double fee){
        //将学费累计到静态变量
        Stu.fee += fee;
    }

    public static double totalFee(){
        return Stu.fee;
    }

}

类方法的经典使用场景
当方法中不涉及到任何和对象相关的成员,则可以将这个方法设计成静态方法,提升开发效率,比如:
工具类中的utils/Math类、Collection集合类。
小结:在程序开发中,往往会讲一些普通的方法,设计成静态方法,这样我们不需要创建实例对象就可以使用了,比图打印一堆数组,冒泡排序,完成某个计算任务等
类方法的注意事项和细节讨论:

  1. 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区:类方法中无this的参数;普通方法中隐含着this参数
  2. 类方法可以由类名调用,也可以通过对象名调用
  3. 普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用
  4. 类方法不能使用和对象相关的关键字,比如:this super 普通方法可以
  5. 类方法(静态方法)中 只能访问静态变量或静态方法
  6. 普通成员方法,既可以访问 非静态成员也可以访问静态成员
    小结:静态方法,只能访问静态的成员,非静态的方法,既可以访问静态成员和非静态的成员,必须遵循访问规则 这里的成员就是方法和属性
package com.chapter10.static_;

/**
 * @author: li
 * @version: 1.0
 */
public class StaticMethodDetail {
    public static void main(String[] args) {
        D.hi();
        // D.say() 非静态成员不能类名.的形式调用
        int n3 = D.n3;  //静态成员变量 类名.变量名调用
    }
}
class D{
    private int n1 = 100;
    private static int n2 = 200;
    public static int n3 = 300;
    public void say(){
        //非静态方法 普通成员方法
    }
    public static void hi(){
        //类方法中不允许使用和对象相关的关键字 比如下:会报错
//        this.n1;
//        super.toString();
    }
    //静态方法只能访问静态成员
    public static void hello(){
        System.out.println(n2);
        System.out.println(D.n2);
        //System.out.println(this.n2); this不能使用
        hi(); //可以使用 静态方法
        //say(); 非静态方法 不能使用
    }
    //普通成员方法,既可以访问非静态成员,也可以访问静态成员
    //非静态方法可以访问静态成员和非静态成员
    public void helli(){
        System.out.println(n1);
        System.out.println(n2);
        say();
        hello();
    }
}

课堂练习
pass

单例设计模式

基本介绍

什么是设计模式

  1. 静态方法和属性的经典使用
  2. 设计模式是在大量实践中总结和理论化之后优选的代码结构,编程风格、以及解决问题的思考方式。设计模式就行是经典的棋谱,不通的棋局,我们用不同的棋谱,免去我们再思考和摸索
    什么是单例设计模式
    单例(单个实例)
  3. 所谓类的单例设计模式,就是采用一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个区的其对象实例的方法
  4. 单例模式有两种: 1.饿汉式 2.饱汉式

单例模式应用实例

步骤:

  1. 构造器私有化,防止被new
  2. 类的内部创建对象
  3. 向外暴露一个静态的公共方法
    代码实现
  4. 饿汉式
package com.chapter10.singl_;

/**
 * @author: li
 * @version: 1.0
 */
public class SingTon01 {
    public static void main(String[] args) {

    }
}
//有一个类 FirlFriend  只能有一个女朋友
class GirlFriend{
    //关于饿汉式 实现单例模式
    //补充说明:对象,通常量级较大,饿汉式存在一个问题 他会创建对象,但是这个对象可能没被使用
    private String name;
    //2.在类的内部直接创建对象 该对象是static
    //说明: 为了能够在静态方法中,返回gf对象 所以需要将他修饰为static(静态方法只能访问类中的静态成员)
    private static GirlFriend gf = new GirlFriend("红");
    //步骤
    //1.构造器私有化
    private GirlFriend(String name) {
        System.out.println("私有构造器被调用");
        this.name = name;
    }
    //3.提供一个公共的static方法,返回gf对象
    public static GirlFriend getGf(){
        return gf;
    }

    @Override
    public String toString() {
        return name;
    }
}
  1. 懒汉式
package com.chapter10.singl_;

/**
 * @author: li
 * @version: 1.0
 */
public class SingleTon02 {
    public static void main(String[] args) {
//        new Cat("蛋黄");
//        System.out.println(Cat.n1);
        Cat instance = Cat.getInstance();
        System.out.println(instance);
        //再次调用getinstance
        Cat instance2 = Cat.getInstance();
        System.out.println(instance2);
        System.out.println(instance == instance2); //true 
        //指向同一个内存地址
    }
}
//希望在程序运行中只创建一个Cat对象
//使用单例模式
class Cat{
    //懒汉式说明:
    // 只有当用户使用getinstance方法时,才返回cat对象,
    // 后面在此调用时,会返回上次创建的cat对象,从而保证单例
    private String name;
    public static int n1 = 99;
    //2.定义一个static静态属性对象
    private static Cat cat; // 初始化时 默认为null
    //步骤:
    //1.构造器私有化
    private Cat(String name) {
        this.name = name;
    }
    //3.提供一个public 的static方法,可以返回一个Cat对象
    public static Cat getInstance(){
        if (cat == null){//如果没有创建cat对象
            cat = new Cat("小可爱");
        }
        return cat;
    }

    @Override
    public String toString() {
        return name;
    }
}

饿汉式 懒汉式 比较

  1. 二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建
  2. 饿汉式不存在线程安全问题,懒汉式存在线程安全问题
  3. 饿汉式存在浪费资源的可能,因为如果程序员一个对象实例都没哟偶使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题
  4. 在javaSE标准类中,java.lang.Runtime就是经典的单例模式

final关键字

基本介绍

final中文意思 最后的最终的;final可以修饰类、属性、方法和局部变量;当某些情况下,程序员可能有如下需求,就会使用到final:

  1. 当不希望类被继承时,可以有final修饰
  2. 当不希望父类的某个方法被子类覆盖/重写(override)时,可以使用final关键字修饰
  3. 当不希望类的某个属性的值被修改,可以使用final修饰
  4. 当不希望某个局部变量被修改,可以使用final修饰
    代码实现上述描述:
package com.chapter10.final_;

/**
 * @author: li
 * @version: 1.0
 */
public class Final01 {
    public static void main(String[] args) {

    }
}
//如果我们要求A类不能被其他类继承,可以使用final修饰A类
final class A{}
//class B extends A{}  报错:不能继承一个final修饰的类
class C{
    //如果我们要求hi不能被子类重写,可以使用final修饰hi方法
    public final void hi(){}
}
class D extends C{
    //当父类方法被final修饰后  子类无法重写改方法 否则会报错
//    @Override
//    public void hi() {
//        super.hi();
//    }
}
//当不希望某个类的某个属性值被修改,可以使用final修饰
class E{
    //被final修饰一般是常量对象,也因此要大写
    public final String STR = "nihao";
}
//当不希望某个局部变量被修改,可以使用fianl修饰
class F{
    public void he(){
        //局部的常量 局部常量 要大写
        final int TCR = 10;
    }
}

final使用注意事项和细节讨论

  1. final装饰的属性又叫常量,一半用XX_XX_XX来命名 例如:GIANT_TCR_ADV3;
  2. final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一【选择一个位置赋初值即可】
  • 定义时:如 public final double TCR_ADV_SL = 0.08;
  • 在构造器中
  • 在代码块中
  1. 如果final修饰的属性是静态的,则初始化的位置只能是:
  • 定义时;
  • 在静态代码块中 不能再构造器中赋值
  1. final类不能继承,但是可以实例化对象
  2. 如果类不是final类,但是含有final方法,该方法虽然不能被重写,但是可以被继承
    代码实现上述说明细节:
package com.chapter10.final_;

/**
 * @author: li
 * @version: 1.0
 */
public class FinalDetail01 {
    public static void main(String[] args) {

    }
}
class AA{
    //1.final修饰的属性在定义时必须有初始值,初始值的定义位置:
    //1.1 在定义时候
    public final double GIANT_OCR = 0.001;
    //2.2 再构造器中
    public final double GIANT_SCR;

    public AA(double GIANT_SCR) {
        this.GIANT_SCR = 0.003;
    }
    //1.3 在代码块中
    public final double GIANT_TCR;
    {
        GIANT_TCR = 0.002;
    }
}
class BB{
    //2 如果final修饰的属性是静态的,则初始位置只能是定义时、静态代码块中
    //不能再构造器中赋值
    public static final double GIANT_OCR = 0.001;
    public static final double GIANT_SCR;
    static {
        GIANT_SCR = 0.002;
    }
}
//3.final类不不能继承 但是可以实例化对象
final class CC{}
//class DD extends CC{}

//4.如果类不是final的,但是含有final方法,则该方法不可以重新,但是可以继承
class DD{
    public final void cal(){
        System.out.println("cal()方法");
    }
}
class EE extends DD{
    
}
  1. 一般来说,如果一个类已经是final类了,就没有必要在建方法修饰成final方法
  2. final不能修饰构造函数(构造方法)
  3. final 和static 往往搭配使用,效率更高,不会导致类的加载,编译器底层做了优化处理
  4. 包装类:Integer Double Float Boolean 等都是final类,String也是final类
package com.chapter10.final_;

/**
 * @author: li
 * @version: 1.0
 */
public class FinalDetail02 {
    public static void main(String[] args) {
        System.out.println(BBB.num);
    }
}
class BBB{
    //final static往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理
    public final static int num = 1000;
    static {
        System.out.println("BBB静态代码块");
    }
}
final class AAA{
    //一般来说 如果一个类已经是final类,就没必要再讲方法修饰成final方法
//    public final void say(){}
}

final应用实例

编写一个程序,能够就算圆形的面积。要求圆周率为3.14 赋值的位置 3个 方式都写一下

package com.chapter10.final_;

/**
 * @author: li
 * @version: 1.0
 */
public class FinalExercise01 {
    public static void main(String[] args) {
        Circle circle = new Circle(1);
        System.out.println(circle.getArea());
    }
}
class Circle{
    private double radius;
//    private final double PI = 3.14;
    private final double PI;

    public Circle(double radius) {
        this.radius = radius;
//        this.PI = 3.14;
    }
    {
        PI = 3.14;
    }
    public double getArea(){
        return PI*radius*radius;
    }
}

public int addOne(final int x) { //下面的代码是否有误,为什么? 1min
++x; //错误,原因是不能修改 final x 的值
return x + 1; //这里是可以.
}
}

抽象类

当父类的某些方法,需要声明,但是又不能确定如何实现时,就可以将其声明为抽象方法,那么这个类就是抽象类

package com.chapter10.abstract_;

/**
 * @author: li
 * @version: 1.0
 */
public class Absyract01 {
    public static void main(String[] args) {
        
    }
}
abstract class Animal{
    private String name;

    public Animal(String name) {
        this.name = name;
    }
    //思考:这里的eat 你实现了 其实没有什么意义
    //即,弗雷方法的不确定性
    //考虑将该方法设计为抽象(abstract)方法
    //所谓的抽象方法就是没有实现的方法
    //所谓没有实现的方法就是指梅伊欧方法体
    //当一个类中存在抽象方法时,需要该类声明为abstract类
    //一般来说,抽象类会被继承,有其子类来实现抽象方法
    public void eat(){
        System.out.println("这个动物不确定吃什么");
    }
}

抽象类快速入门

当父类的一些方法不能确定时,可以使用abstract关键字来修饰该方法,这个方法就是抽象方法,用abstract来修饰该类就是抽象类
如何把上文中的Animal做成抽象类,并让子类Cat类实现

abstract class Animal{
public String name;
abstract public void cry()
}

抽象类的介绍

  1. 用abstract关键字来修饰一个类时,这个类就是抽象类 访问修饰符 abstract 类名{}
  2. 用abstract关键字来修饰一个方法时,这个方法就是抽象方法 访问修饰符 abstract 返回类型 方法名(形参列表);后面没有方法体
  3. 抽象类的交织更多作用在于设计,是设计者设计好后,让子类继承并实现抽象类
  4. 抽象类,是面试比较爱问的问题,在框架和设计模式使用较多

抽象类使用的注意事项和细节讨论

  1. 抽象类 不能被实例化
  2. 抽象类不一定包含abstract方法。也就是说,抽象类中可以没有abstract方法
  3. 一旦类中包含了abstract方法,则这个类必须声明成abstract
  4. abstract只能修饰类和方法 不能修饰属性和其他的
package com.chapter10.abstract_;

/**
 * @author: li
 * @version: 1.0
 */
public class AbstractDetail01 {
    public static void main(String[] args) {
        A a = new A();  //报错
    }
}
//1. 抽象类不一定要包含abstract方法,也就是说 抽象类可以没有abstract方法,还可以有实现的方法
abstract class A{
    public void hello(){
        System.out.println("你好啊");
    }
}
//2. 一旦类包含了abstract方法,则这个类必须声明为abstract
abstract class B {
    public abstract void hi();
}
//3. abstract 只能修饰类和方法,不能修饰属性和其它的
class C{
    //public abstract int n2 = 100;
}
  1. 抽象类可以有任意成员【抽象类的本质还是类】。比如:非抽象方法、构造器、静态属性等等
  2. 抽象方法不能有主体,即方法体不能实现
  3. 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,但如果它本身也是一个abstract类,则可以不实现或者实现部分
  4. 抽象方法不能使用private final 和 static修饰,因为这些关键字都是和重写相违背的
package com.chapter10.abstract_;

/**
 * @author: li
 * @version: 1.0
 */
public class AbstractDetail02 {
    public static void main(String[] args) {
        
    }
}
//1.抽象方法不能使用private final static修饰,因为这些关键字都是和重写违背的
abstract class H{
    //private abstract void say();
    //final abstract void say();
    //static abstract void say();
    public abstract void say();
}
//2.如果一个类继承了抽象类,则必须实现抽象类的所有抽象方法
abstract class E{
    public abstract void hi();
    public abstract void ha();
    public abstract void ho();
}
class F extends E{
    @Override
    public void hi() {
        System.out.println("hi");
    }

    @Override
    public void ha() {
        System.out.println("ha");
    }

    @Override
    public void ho() {
        System.out.println("ho");
    }
}
//当子类也同样声明为抽象类时,则不需要全部实现 可以实现全部/部分/不实现
abstract class G extends E{
    @Override
    public void hi() {
        System.out.println("hi");
    }
}
//3.抽象类的本质还是类 他可以具有类的各种成员
abstract class D{
    public int n1 = 10;
    public static int n2 = 20;
    final static int n3 = 30;
    public abstract void hi();
    public String ho(){
        return "ho";
    }
}

抽象类最佳实践-模板设计模式

基本介绍

抽象类体现的就是一种模板模式设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上回保留抽象类的行为方式

模板设计模式能解决的问题
  1. 当功能内部一部分实现是确定的,一部分是不确定的。这时可以把不确定的部分暴露出去,让子类实现
  2. 编写一个抽象父类,弗雷提供多个子类的通用方法,并把一个或者多个方法留给其子类实现,就是一种模板模式
最佳实践
  1. 有多个类,完成不同的任务job
  2. 要求统计得到各自完成任务的时间
    分析:
  3. 先用最容易想到的方法--代码实现
  4. 分析问题,提供使用模板设计模式
package com.chapter10.abstract_;

/**
 * @author: li
 * @version: 1.0
 */
public class TemplateWorkFocus {
    public static void main(String[] args) {
        AA aa = new AA();
        aa.calculateTime();

        BB bb = new BB();
        bb.calculateTime();
    }
}
//抽象类-模板设计模式
abstract class Template{
    //抽象方法
    public abstract void job();
    public void calculateTime(){
        //实现方法,调用job方法
        //得到开始时间
        long start = System.currentTimeMillis();
        job();
        /**
         * AA的实例对象 aa调用父类的calculateTime方法
         * 在calculateTime方法内部调用job方法 
         * aa的运行类型是AA 也因此job调用的是aa的重写父类的方法*/
        long end = System.currentTimeMillis();
        System.out.println("AA 执行时间 " + (end - start));
    }
}
class AA extends Template{
    //实现Template的抽象方法 job

    @Override
    public void job() {
        long num = 0;
        for (int i = 0; i < 8000000; i++) {
            num *= i;
        }
    }
}
class BB extends Template{
    @Override
    public void job() {
        long num = 0;
        for (int i = 0; i < 8000000; i++) {
            num += i;
        }
    }
}

接口

快速入门

pass

基本介绍

接口就是给出一些没有实现的方法,封装到一期,到某个类要使用的时候,在根据具体情况把这些方法写出来,语法:
interface 接口名{
属性
抽象方法
}
class 类名 implements 接口{
自己的属性;
自己的方法
必须要实现的接口的抽象方法
}

小结

接口是更加抽象的抽象的类,抽象类里的方法可以有方法体,接口里面所有的方法都没有方法体【jdk7.0】 接口体现了程序设计的堕胎和高聚合低耦合的设计思想。
特别说明:jdk8.0后接口可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现

深入讨论

接口的概念不算太难 难的是不知道什么时候使用接口,下面举几个例子:

  1. 如果现在我们需要制造战斗机,武装直升机。专家只需要将飞机的需要的功能/规格定下来即可,然后让别的人具体实现就可
  2. 如果说现在有一个项目经理,管理三个程序员,功能开发一个软件,为了控制和管理软件,项目经理可以定义一些接口,然后由程序员具体实现
    关于2.的案例代码展示
package com.chapter10.interface_;

/**
 * @author: li
 * @version: 1.0
 */
public interface DBInterface {
    //项目经理定义了方法
    public void connect();
    public void close();
}
package com.chapter10.interface_;

/**
 * @author: li
 * @version: 1.0
 */
public class MysqlDB implements DBInterface{
    //A程序员操作
    @Override
    public void connect() {
        System.out.println("连接MYSAL");
    }

    @Override
    public void close() {
        System.out.println("关闭MYSQL");
    }
}
package com.chapter10.interface_;

/**
 * @author: li
 * @version: 1.0
 */
public class OracleDB implements DBInterface{
    //B程序员操作

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

    @Override
    public void close() {
        System.out.println("关闭ORACLE");
    }
}
package com.chapter10.interface_;

/**
 * @author: li
 * @version: 1.0
 */
public class Interface03 {
    public static void main(String[] args) {
        MysqlDB mysqlDB = new MysqlDB();
        tet(mysqlDB);
        OracleDB oracleDB = new OracleDB();
        tet(oracleDB);
    }
    public static void tet(DBInterface db){
        db.connect();
        db.close();
    }
}

注意事项和细节

  1. 接口不能被实例化
  2. 接口中所有的方法都是public方法,接口中抽象方法,可以不用abstract修饰
  3. 一个普通类实现接口,就需要将改接口的所有方法都实现
  4. 抽象类实现接口,可以不用实现接口的方法
package com.chapter10.interface_;

/**
 * @author: li
 * @version: 1.0
 */
public class InterfaceDetail01 {
    public static void main(String[] args) {
        //1.接口不能被实例化
        //IA ia = new IA();
    }
}

interface IA{
    //2.接口中所有的方法是public方法,接口中的抽象方法可以不应abstract修饰
    public abstract void say1();
    public void say2();
    //private void say3();
}
//3.一个普通类实现接口,就必须把接口中的所有方法都实现,可以使用alt+enter快捷键
class Cat implements IA{

    @Override
    public void say1() {
        
    }

    @Override
    public void say2() {

    }
}
//4.抽象类去实现接口时,可以不实现接口的抽象方法
abstract class AAAA implements IA{
    //抽象类内部为空 编译阶段无任何异常
}
  1. 一个类可以同时实现多个接口
  2. 接口中的属性 只能是final的 而且是public static final 修饰;比如:int n1 = 1;实际上是 public static int n1 = 10;
  3. 接口中属性的访问形式是:接口名.属性名
  4. 接口不能继承其他类,但是可以继承多个别的接口 interface A extends B,C{}
  5. 接口的装饰符,只能是public和默认,这点和类的装饰符是一样的
package com.chapter10.interface_;

/**
 * @author: li
 * @version: 1.0
 */
public class InterfaceDetail02 {
    public static void main(String[] args) {
        //证明 1.的说明
        System.out.println(IB.n1); //证明属性是static的
        // IB.n1 = 100;  无法复制修改 证明是final的 
    }
}
interface IB{
    //1.接口中的属性,只能是final的,而且是 public final static修饰的 
    int n1 = 10; //等价于 public final static int n1 = 10;
    void hi();
}
interface IC{
    void say();
}
//2.接口不能继承其他的类,但是可以继承多个别的接口
interface ID extends IB,IC{
    
}
//3.接口的修饰符,只能是public 和默认,这点和类的修饰符是一样的
interface IE {}

// 4.一个类 同时可以实现多个接口
class Pig implements IB,IC{

    @Override
    public void hi() {
        
    }

    @Override
    public void say() {

    }
}

课堂练习
inteerface A{
int a = 10; // 等价于 public static final int a = 23;
}
class B implements A {
}
main 中:
B b = new B(); //正确
sout(b.a) //正确 子类没有 往父类中去查找
sout(A.a) //正确 类名.变量名 访问静态属性
sout(B.a) //正确 静态属性也是属性,子类没有回去父类查找

实现接口VS继承类

实现接口和继承类的概念我确实有点迷茫,这是在说啥?不是接口和继承吗?怎么跑出个实现接口和继承类,通过字面意思感觉大概好像似乎了解,那么,代码展示吧,加深理解;

package com.chapter10.interface_;

/**
 * @author: li
 * @version: 1.0
 */
public class ExtendsVsImplements {
    public static void main(String[] args) {
        LittleMonkey xunwukong = new LittleMonkey("熏悟空");
        xunwukong.flying();
        xunwukong.swimming();
    }
}
//猴子类
class Monkey{
    private String name;

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

    public void climbing(){
        System.out.println(name + "会爬树");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
//接口
interface Fish{
    void swimming();
}
interface Birdable{
    void flying();
}
//小结:当子类继承了父类,就自动拥有了父类的功能
//如果子类需要扩展,可以通过实现接口的方式扩展
//可以理解 实现接口是对 java单继承机制的一种补充

class LittleMonkey extends Monkey implements Fish,Birdable{
    public LittleMonkey(String name) {
        super(name);
    }

    @Override
    public void swimming() {
        System.out.println(getName() + "通过学习,可以游泳了");
    }

    @Override
    public void flying() {
        System.out.println(getName() + "通过学习,可以飞行了");
    }
}
接口和继承 解决的问题不同

继承的价值主要在于:解决代码的复用性和可维护性
接口的价值主要在于:设计,设计好各种规范(方法),让其他类去实现这些方法。即更加灵活

  • 接口比继承更加灵活:
    接口比继承更加灵活,继承满足is-a的关系,而接口只需要满足like-a的关系
  • 接口在一定成都上实现代码解耦【即:接口规范性+动态绑定机制】

接口的堕胎特性

  1. 多态参数:
    前面的USB接口,入参为(UsbInterface usb)既可以接收手机对象,又可以接受相机对象,这就体现了接口多态(接口的引用可以指向实现了接口的类的对象)
package com.chapter10.interface_;

/**
 * @author: li
 * @version: 1.0
 */
public class InterfacePolyParameter {
    public static void main(String[] args) {
        //接口多态的体现
        //接口类型的变量if01可以指向 实现了IF接口类的对象实例
        //那么,有的朋友可能要问了,接口不是不能实例化嘛,为啥能IF if01呢?
        //这个问题,问得好啊,首先 实例化其实就是new XXX 显然这里没有new IF的操作
        //举个栗子 我玩完不给钱就不算嫖了啊
        IF if01 = new Monster();
        if01 = new Car();
        
        //继承体现的多态
        //父类类型的变量a 可以指向 继承AAA的子类的对象实例
        AAA a = new BBB();
        a = new CCC();
    }
}
interface IF{}
class Monster implements IF{}
class Car implements IF{}
class AAA{}
class BBB extends AAA{}
class CCC extends AAA{}
  1. 多态数组
    演示案例:给Usb数组中中存放 手机和相机对象,手机对象有特有的call方法,遍历Usb数组,如果是手机对象,除了调用Usb接口定义的方法还需要调用本身特有的call方法
package com.chapter10.interface_;

/**
 * @author: li
 * @version: 1.0
 */
public class InterfacePolyArr {
    public static void main(String[] args) {
        //新建一个接口Usb类型的多态数组
        Usb[] usbs = new Usb[2];
        usbs[0] = new Phone_();
        usbs[1] = new Camera_();
        /*给Usb数组中,存放手机和相机对象,手机对象还有一个特有的call方法
        * 遍历Usb数组,如果是Phone对象,除了调用Usb接口定义的方法外
        * 还需要调用手机对象特有的call方法*/
        for (int i = 0; i < usbs.length; i++) {
            usbs[i].work(); // 这里就是动态绑定机制
            //判断usb[i]的运行类型是Phone_
            if (usbs[i] instanceof 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("相机正在工作");
    }
}

3.接口存在多态传递现象

package com.chapter10.interface_;

/**
 * @author: li
 * @version: 1.0
 */
public class InterfacePolyPass {
    public static void main(String[] args) {
        //接口类型的变量,可以指向,实现了该接口的类的对象实例
        IG ig = new Teacher();
        //如果 IG继承自IH接口,而Teacher实现了IG接口
        //那么,实际上相当于Teacher类也实现了IH接口
        //这就是所谓的 接口多态传递想想
        IH ih = new Teacher();
    }
}
interface IH{
    void hi();
}
interface IG extends IH{}
class Teacher implements IG{
    @Override
    public void hi() {
        System.out.println("say hi");
    }
}

课堂练习

package com.chapter10.interface_;

/**
 * @author: li
 * @version: 1.0
 */
public class InterfaceExercise02 {
    public static void main(String[] args) {
        C c = new C();
        c.getX();
    }
}
interface A {
    int x = 0;  // 等价于 public static final int x = 0;
}
class B{
    int x = 1;  //这是B类的普通属性
}
class C extends B implements A{
    public void getX(){
        //System.out.println(x);
        //错误:说明:没有指定是哪个x
    //如何明确指定x:
    //1.访问接口的x就 A.x
        System.out.println(A.x);
    //2.访问父类的x 就super.x
        System.out.println(super.x);
    }
}

内部类

  1. 如果定义类在局部位置(方法中,代码块中):(1)局部内部类 (2)匿名内部类
  2. 定义在成员位置: (1)成员内部类 (2)静态内部类

基本介绍

一个类的内部有完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员【属性、方法、构造器、代码块、内部类】,内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系,注意:内部类是学习的难点,同时也是在重点,后面看底层源码时候,有大量的内部类

基本语法

class Outer{//外部类
class Inner{//内部类
}
}
class Other{}//外部其它类

快速入门案例

package com.chapter10.innerclass_;

/**
 * @author: li
 * @version: 1.0
 */
public class InnerClass01 {
    public static void main(String[] args) {
        
    }
}
class Outer{
    private int n1 = 100;

    public Outer(int n1) {
        this.n1 = n1;
    }
    public void m1(){
        System.out.println("m1");
    }
    {
        System.out.println("代码块");
    }
    class Inner{ //内部类 在Outer内部
        
    }
}

内部类的分类

  1. 定义在外部类局部位置上(比如说方法内)
  • 局部内部类(有类名)
  • 匿名内部类(没有类名 ※)
  1. 定义在外部类的成员位置上:
  • 成员内部类(没有static修饰)
  • 静态内部类(使用static修饰)

局部内部类

说明:

  1. 可以直接访问外部类的局部位置,比如方法中,并且有类名
  2. 不能添加访问修饰符,因为他的地位就是一个局部变量。局部变量是不能使用修饰符的,但是可以使用final修饰,因为局部变量也可以使用final
  3. 作用域:仅仅在定义他的方法或代码块
  4. 局部内部类---访问---外部类的成员【访问方式:直接访问】
  5. 外部类---访问---局部内部类的成员【访问方式:创建对象,再访问(注意:必须在作用域内)】
    记住:
  • 局部内部类定义在方法中/代码块
  • 作用域在方法体或者代码块中
  • 本质仍是一个类
  1. 外部其他类---不能访问---局部内部类(因为 局部内部类地位是一个局部变量)
  2. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
    代码演示:
package com.chapter10.innerclass_;

/**
 * @author: li
 * @version: 1.0
 */
public class LocalInnerClass {
    public static void main(String[] args) {
        Outer02 outer02 = new Outer02();
        outer02.m1();
        System.out.println("outer02的hashcode" + outer02);
    }
}
class Outer02{ // 外部类
    private int n1 = 100;
    public int n2 = 200;
    private int n3 = 300;
    private void m2(){//私有方法
        System.out.println("Outer的m2方法");
    }
    public void m1(){//局部内部类,本质还是一个类
        //1.局部内部类是定义在外部类的局部位置,通畅是在方法、代码块中
        //3.不能添加访问修饰符,但是可以使用final (使用或者不写)
        //4.作用域:仅仅在定义他的方法或代码块中
        final class Inner02{
            //2.可以直接访问外部类的所有成员,包含私有的
            private int n1 = 800;
            public void f1(){
                //5.局部内部类可以直接访问外部类的成员 比如下面  外部类的n1和m2()
                m2();
                System.out.println("外部类的n2" + n2);
                System.out.println("外部类的n3" + n3);
                //7.如果外部类和局部内部类的成员重名时,默认遵循就近原则
                //如果你真的需要去访问外部类的成员,使用 外部类名.this.成员去访问
                System.out.println("你猜猜这个n1是多少" + n1);
                System.out.println("n1" + n1 + "外部类的n1" + Outer02.this.n1);
            }
        }
        //6.在外部类的方法中,可以创建Inner02对象,然后调用方法即可
        Inner02 inner02 = new Inner02();
        inner02.f1();
    }
}

匿名内部类的使用

  1. 匿名内部类本质也是类
  2. 内部类的一种
  3. 该类没有名字
  4. 同时还是一个对象
    说明:匿名内部类定义在外部类的局部位置,比如方法中,并且没有类名
    基本语法:
    new 类或接口(参数列表)
package com.chapter10.innerclass_;

/**
 * @author: li
 * @version: 1.0
 */
public class AnonymousInnerClass {
    /**
     * 演示匿名内部类的使用*/
    public static void main(String[] args) {
        Outer04 outer04 = new Outer04();
        outer04.method();
        Outer04 outer03 = new Outer04();
        outer03.method();
    }
}
class Outer04{//外部类
    private int n1 = 10; //属性
    public void method(){ // 方法
        //基于接口的匿名内部类
        //1.需求:想使用IA接口,并创建对象
        //2.传统方式,是写一个类,实现改接口,并创建对象
        //3.现在Tiger、Dog类只是使用一次,后面再不使用
        //4.使用匿名内部类简化开发
        //5.tiger 的编译类型是IA tiger的运行类型是Outer04$1  这个就是匿名内部类的名字
        //6.JDK底层在创建匿名内部类Outer04$1,立即马航创建了Outer04$1实例,并且把地址返回给tiger
        IA tiger = new IA() {
            @Override
            public void cry() {
                System.out.println("老虎在哭");
            }
        };
        System.out.println("tiger的运行类型是 = " + tiger.getClass());
        tiger.cry();
        tiger.cry();
        tiger.cry();
        //8.匿名内部类使用一次,就不能再使用了
        //这个没太懂 网上查了查 看懂了 但是没太理解
        IA tager = new IA() {
            @Override
            public void cry() {

            }
        };
        System.out.println("tiger的运行类型是 = " + tiger.getClass());
        tiger.cry();

        //基于类的匿名内部类
        //分析
        //1.father的编译类型是Father
        //2.father的运行类型是Outer04$2
        //3.底层源码会创建内部类Outer04$2继承father类并重写父类方法
        //同时也直接返回了内名内部类 Outer04$2的对象
        //注意 jack 这个参数列表会传递给构造器
        Father father = new Father("jack"){
            @Override
            public void test() {
                System.out.println("基于类的不匿名内部类重写了test方法");
            }
        };
        System.out.println(father);
        father.test();
        //基于抽象类的匿名内部类
        Animal animal = new Animal() {
            @Override
            void eat() {
                System.out.println("抽象类的方法被基于抽象类的匿名内部类实现并重写");
            }
        };
        animal.eat();
    }



}
interface IA{//接口
    public void cry();
}
//传统方法 定义一个类实现IA接口 重写IA的方法 实例化类调用方法

class Father {
    public Father(String name) {
        System.out.println("接收到的name=" + name);
    }
    public void test(){}
}
abstract class Animal{
    abstract void eat();
}
  1. 匿名内部类的语法比较奇特,请各位注意,因为匿名内部类既是一个类的定义,同时本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法
  2. 可以直接访问外部类的所有成员,包含私有的
  3. 不能添加访问修饰符,因为他的位置就是一个局部变量
  4. 作用域:仅仅在定义它的方法或代码块中
  5. 匿名内部类---访问---外部类成员[访问形式 直接访问]
  6. 外部其他类---不能访问---匿名内部类 [因为匿名内部类地位是一个局部变量]
  7. 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
package com.chapter10.innerclass_;

/**
 * @author: li
 * @version: 1.0
 */
public class AnonymousInnerClassDetail {
    public static void main(String[] args) {
        Outer05 outer05 = new Outer05();
        outer05.f1();
        //外部其他类--不能访问--匿名内部类
        System.out.println(outer05);
    }
}
class Outer05{
    private int n1 = 99;
    public void f1(){
        //创建一个居于类的匿名内部类
        // 不能添加访问修饰符,因为他的地位就是一个局部变量
        //作用域:仅仅定义它的方法或代码块中
        Person p = new Person(){
            private int n1 = 88;

            @Override
            public void hi() {
                //可以直接访问外部类的所有成员 包含私有的
                //如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,
                //默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
                System.out.println("匿名内部类重写了hi方法n1=" + n1 + "外部内的n1" + Outer05.this.n1);
                //Outer05.this 就是调用f1的对象
                System.out.println("outer05.this hashcode = " + Outer05.this);

            }
        };
        p.hi();  //动态绑定机制 运行类型是 Outer05$1

    }
}
class Person{
    public void hi(){
        System.out.println("Person hi");
    }
    public void ok(String str){
        System.out.println("Person ok" + str);
    }
}

匿名内部类最佳实战

当作实参直接传递,简洁高效

package com.chapter10.innerclass_.abstract_;

/**
 * @author: li
 * @version: 1.0
 */
public class InnerClassExecrise01 {
    public static void main(String[] args) {
        //传统方法
        f1(new Picture());
        //当作实参直接传递,简洁高效
        //这块有点问题  需要报读查一下
        f1(new IL()){
            @Override
             public void show(){
                System.out.println("这是一幅名画");
            }
        }

    }
    //静态方法,型参是接口类型
    public static void f1(IL li){
        li.show();
    }
}
//接口
interface IL{
    void show();
}
//类-实现IL-编程领域(硬编码)
class Picture implements IL{
    @Override
    public void show() {
        System.out.println("这是一幅名画");
    }
}

匿名内部类课堂练习

  1. 有一个铃声接口bell 里面有个ring方法
package com.chapter10.innerclass_.abstract_;

/**
 * @author: li
 * @version: 1.0
 */
public class InnerClassExercise02 {
    public static void main(String[] args) {
        /*
        1.有一个铃声接口 Bell,里面有个 ring 方法。(右图)
        2.有一个手机类 Cellphone,具有闹钟功能 alarmClock,参数是 Bell 类型(右图)
        3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
        4.再传入另一个匿名内部类(对象),打印:小伙伴上课了
        */
        CellPhone cellPhone = new CellPhone();
        cellPhone.alarmClock(new Bell() {
            @Override
            public void ring() {
                System.out.println("起床");
            }
        });
    }
}
interface Bell{
    void ring();
}
class CellPhone{
    public void alarmClock(Bell bell){
        //形参是Bell接口类型
        System.out.println(bell.getClass());
        bell.ring(); //动态绑定
    }
}

成员内部类的使用

成员内部类是定义在外部类的成员位置,没有static修饰

  1. 可以直接访问外部类的所有成员,包含私有的
  2. 可以添加任意访问修饰符(public protected 默认 private),因为它的地位就是一个成员
  3. 作用域:和外部类的其他成员一样,为为整个类体
  4. 成员内部类--访问--外部类成员(比如 属性)【访问形式:直接访问】
  5. 外部类---访问---成员内部类【访问形式:创建对象再访问】
  6. 外部其他类--访问--成员内部类
  7. 如果外部类和内部类的成员重名时,内部类访问的话,遵循就近原则,如果想访问外部类的成员,可以使用(外部类名.this.成员)去访问
    package com.chapter10.innerclass_;

/**

  • @author: li

  • @version: 1.0
    */
    public class MemberInnerClass01 {
    public static void main(String[] args) {
    Outer08 outer08 = new Outer08();
    outer08.t1();
    //外部其他类 使用成员内部类的三种方式
    //1. 第一种方式
    Outer08.Inner08 inner08 = outer08.new Inner08();
    inner08.say(); //语法 不用纠结 相当于 把new Inner08()当作是 outer08的成员

     //2.第二种方式  在外部类中 编写一个方法,可以返回Inner08对象
     Outer08.Inner08 inner081 = outer08.getInner08Instance();
     inner081.say();
    

    }
    }
    class Outer08{//外部类
    private int n1 = 10;
    public String name = "张三";
    private void hi(){
    System.out.println("hi");
    }
    //1. 注意 成员内部类 是定义在外部类的成员位置上
    //2.可以添加任意访问修饰符(public protected 默认 private)因为他的地位就是一个成员
    public class Inner08{ // 成员内部类
    private double asl = 99.8;
    private int n1 = 66;
    public void say(){
    // 可以直接访问外部类的所有成员,包含私有的
    //如果成员内部类的成员和外部类的成员重名,会遵循就近原则
    // 如果想要访问外部类的重名成员 使用 外部类名.this.成员 访问
    System.out.println("外部类的n1" + Outer08.this.n1);
    System.out.println("内部类的n1" + n1);
    }
    }
    //方法 返回一个Inner08的实例
    public Inner08 getInner08Instance(){
    return new Inner08();
    }

    //写方法
    public void t1(){
    //使用成员内部类
    //创建成员内部类的对象,然后使用相关的方法
    Inner08 inner08 = new Inner08();
    inner08.say();
    //System.out.println(inner08.say());
    }
    }

#### 静态内部类的使用

说明:静态内部鳄类是定于在外部类的成员位置,并且有static修饰
1. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
2. 可以添加任意访问修饰符(public protected 默认 private),因为他的位置就是一个成员
3. 作用域:同其他的成员 为整个类体
4. 静态内部类--访问--外部类(比如:静态属性)【访问方式:直接访问所有静态成员】
5. 外部类--访问--静态内部类 访问方式:创建对象 再访问
6. 外部其他类--访问--静态内部类
7. 如果外部类和静态内部类的成员重名时,静态内部类访问的时候,遵循就近原则,如果想要访问逮捕类的成员,则可以使用(外部类名.成员)去访问

package com.chapter10.innerclass_;

/**

  • @author: li

  • @version: 1.0
    */
    public class StaticInnerClass01 {
    public static void main(String[] args) {
    //外部其他类 使用静态内部类
    //方式1 因为静态内被雷 是可以通过类名直接访问 (前提是满足访问权限)
    Onter10.Inner10 inner10 = new Onter10.Inner10();
    inner10.say();
    //方式2 编写一个方法 返回静态内部类的实例对象
    Onter10 onter10 = new Onter10();
    Onter10.Inner10 inner101 = onter10.getInner10();
    inner101.say();

     Onter10.Inner10 inner102 = Onter10.getInner10_();
     inner102.say();
    

    }
    }
    class Onter10{ // 外部类
    private int n1 = 10;
    private static String name = "张三";
    private static void cry(){

    }
    //Inner10 就是 静态内部类
    //1.放在外部类的成员位置 2.使用static修饰
    //4.可以添加人与访问修饰符(public protected 默认 private) 因为他的位置是一个成员
    //5.作用域 同其他的成员 为整个类体
    static class Inner10{
    private String name = "法外狂徒";
    public void say(){
    //3.可以访问外部类的所有静态成员,包含私有的,但是不能直接访问非静态成员
    //如果外部类和静态内部类成语那重名时候,遵顼就近原则;想访问外部类成员 使用 外部类名.成员 方式
    System.out.println("外部类的静态成员n1" + Onter10.name);
    }
    }
    //外部类--访问--静态内部类 访问方式 先创建对象 再访问
    public void m1(){
    Inner10 inner10 = new Inner10();
    inner10.say();
    }
    public Inner10 getInner10(){
    return new Inner10();
    }

    public static Inner10 getInner10_(){
    return new Inner10();
    }

}

课堂测试题
public class Test {//外部类
public Test() {//构造器
Inner s1 = new Inner(); 
s1.a = 10;
Inner s2 = new Inner(); 
System.out.println(s2.a);
}
class Inner { //内部类,成员内部类
public int a = 5;
}


public static void main(String[] args) { 
Test t = new Test();
Inner r = t.new Inner();//5 
System.out.println(r.a);//5
}
}
posted @ 2022-09-05 09:47  sakera  阅读(16)  评论(0)    收藏  举报