面向对象高级(黑马)

static静态变量

  • Srudent类
package com.an.a;
//描述学生类
public class Student1 {
    private String name;
    private int age;
    private String gender;
 public static String teacherName;
    public Student1() {
    }

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

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return gender
     */
    public String getGender() {
        return gender;
    }

    /**
     * 设置
     * @param gender
     */
    public void setGender(String gender) {
        this.gender = gender;
    }

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

  • 测试类
package com.an.a;

import java.util.Scanner;

public class StudentTest {

    public static void main(String[] args) {
      Student1 s1 = new Student1("张三",23,"男");
      Student1 s2 = new Student1("李四",24,"女");
      Student1.teacherName="天天老师";//静态变量所有变量共享,可以通过类名赋值
      s1.show();
      s2.show();
    }
}




一个班的老师是所有学生共享

static变量内层图



在堆内存中专门有一个静态区用来储存静态变量和方法

关于一个变量是否可以定义成静态的,这个主要取决于是否共享,但是也要具体的业务场景具体分析

静态方法

工具类

工具类的基本要求


工具类

package com.an.a;
//定义数组工具类
public class ArrayUtil {
    //1.私有化构造方法
  public ArrayUtil(){}
   //2.将所有的方法都定义成静态的
   public static void printArr(int[] arr){//返回整数数组的内容
      //遍历数组重新拼接
       StringBuilder sb = new StringBuilder();
       sb.append("[");
       for (int i = 0; i < arr.length; i++) {
           if(i!=arr.length-1){
               sb.append(arr[i]).append(",");
           }else {
               sb.append(arr[i]);
           }
       }
       sb.append("]");
       System.out.println(sb);
   }
    public static double getAverage(double arr[]){
      double sum=0;
        for (int i = 0; i < arr.length; i++) {
            sum+=arr[i];
        }
        return sum/arr.length;
    }
}

测试类

package com.an.a;

public class ArrayUtilTest {//对于工具类的测试类

    public static void main(String[] args) {
        int [] a= {1,2,3,5,7};
        ArrayUtil.printArr(a);
        double []arr={1.3,4.5,6.8,9.1};
        final double average = ArrayUtil.getAverage(arr);
        System.out.println(average);

    }
}

学生工具类

package com.an.a;

import java.util.ArrayList;

//学生工具类:获取集合中最大的学生年龄
public class StudentUtil {
    //构造方法私有化
    private StudentUtil(){}
    //将方法定义为静态的
    public static int getMaxAgeStudent(ArrayList<Student2> list){
        int max = list.get(0).getAge();//将第一个对象为对照值
        for (int i = 1; i < list.size(); i++) {
            int tem = list.get(i).getAge();
            if(tem>max){
                max=tem;
            }
        }
        return max;
    }
}

测试类

package com.an.a;

import java.util.ArrayList;

public class StudentTest2 {
    public static void main(String[] args) {
        ArrayList<Student2> list = new ArrayList<>();
        Student2 s1 = new Student2("张三",23,"男");
        Student2 s2 = new Student2("李四",24,"男");
        Student2 s3 = new Student2("张三",25,"男");
        list.add(s1);
        list.add(s2);
        list.add(s3);
        final int maxAgeStudent = StudentUtil.getMaxAgeStudent(list);
        System.out.println(maxAgeStudent);

    }
}

学生的javabean类省略

Staitc的注意事项

关于static的三个结论

代码方面理解static

非静态的变量和方法属于对象,而静态的变量和方法由所有的对象共享,而不属于某个对象
1.静态方法里面不能出现this

package com.an.a;

public class Student3 {
    String name;
    int age;
   static String teacherName;
    //this:表示当前方法调用者的地址值
    //这个this是由虚拟机自动赋值的
    //非静态成员方法有一个隐藏的this参数,不能手动赋值由虚拟机统一赋值
    //虚拟机每次将调用者的地址值赋值给成员方法
    public void show1(Student3 this){
        System.out.println(this.name+","+age+","+teacherName);//后面变量都隐藏this
        show2();//此处也省略了this
    }
    public void show2(){
        System.out.println("show2");
    }
    public static void method(/*Student3 this*/){//会报错
        //System.out.println("this"+this);
        System.out.println("静态方法");
    }
}

从内层方面的解释


非静态的和内层有关,当静态的加载到内层中,非静态的还没有加载到内层中,静态的可以互相调用

method方法调用name和teacherName2个变量,到堆内存中去查找这些变量,对象没有被创建不能调用name

重新认识main方法

继承

继承的概述


问题所在,重复的代码太多了

解决方法:将重复的内容定义在另外一个类中



使用继承需要满足is-a的结构




继承的特点和继承体系的设计


不支持多继承的原因





在设计继承的架构的时候,不要一开始就写代码,我们可以通过画图来想清楚继承的结构
利用画图法分析继承结构

package com.an.a;

public class Extends {
    //在设计继承结构之前我们需要画继承结构图
    class Animal{
        public void eat(){
            System.out.println("我正在吃饭哦");
        }
        public void drink(){
            System.out.println("我正在喝水哦");
        }
    }
    class Cat extends Animal{
        public void hitMat(){
            System.out.println("小老鼠,哪里跑");
        }
    }
    class Dog extends Animal{
        public void careHome(){
            System.out.println("坏人不准进我家");
        }
    }
    class BuouCat extends Cat{

    }
    class LihuaCat extends Cat{

    }
    class HashiqiDog extends Dog{
        public void breakHome(){
            System.out.println("我正在拆家");
        }
    }
    class TaidiDog extends Dog{
        public void play(){
            System.out.println("我没事就想蹭一蹭");
        }
    }
}


当父类中的变量和方法用private修饰子类中则无法访问,子类只能访问父类的非私有成员

子类可以继承父类的成员变量和公有的方法,不能继承构造函数,对于私有的成员变量可以继承但是不能直接访问

子类到底能继承父类中的那些内容?(内层图/内层分析工具)

  • 结论

和以前对象内层结构的不同:1.在加载文件的时候会把父类的字节码文件也加载过来,在创建对象时,其堆内存空间会储存一部分从父类继承过来的成员变量

当成员变量被私有化时


其内层结构和没有用private修饰的内层结构基本一样,但是由于其成员变量被private修饰只能在本类中使用,所有在外部无法进行赋值

成员方法是否可以被继承


以前方法调用特点:当该类的对象调用一个方法,会先在本类中寻找该方法,如果本类中没有找到,会到父类中寻找,如果父类中没有找到,将会到父类的父类中寻找该方法,以此反复
以上的观点也是我的误区,

如果我们要调用的方法在A类的顶级父类中,如果这样一层一层的去寻找,这样方法调用的效率将会很坏

java在底层会创立虚方法表进行优化

将会从顶级父类开始,会将这个类中经常要用到的方法抽取出来(这样的方法为虚方法:满足1.非priate 2.非static 3.非final),放到虚方法表中,在继承的时候父类会将自己的虚方法表交给其子类,子类会在父类虚方法表的基础上添加自己的虚方法,然后将虚方法表交给自己的子类,以此反复

当要调用方法时,会首先在该类的虚方法表中寻找这个方法,如果没有找到才会一层一层的向上寻找


可以得出以上结论:只有虚方法才能被子类继承

继承中成员变量和成员方法的访问特点

成员变量的访问特点


就近原则:当打印一个变量,会现在局部的位置找,当局部的位置没有找到,会到本类的成员变量的位置找,当本类的成员变量位置没有找到,将会在父类的成员位置找

这三种变量调用的解决方案

用this和super分别标识本类中成员变量和父类中的成员变量

成员方法的访问特点



成员方法的调用成员变量基本相同都满足 就近原则

方法的重写

当父类的方法被子类重写,子类对象调用的就是重写后的方法(重写的结论),而方法的调用遵循就近原则,如果是this调用会先在本类中寻找-----然后到父类中寻找,当出现方法重写的场景的时候,2种方法调用方法最后的结论完美的一致

方法重写的应用场景:当父类中的方法不能满足子类现在的需求时,我们就需要把这个方法进行重写,重写的时候要加上@Override注解,可以帮助我们检查重写的语法是否正确

方法重写的本质

方法重写的本质就是覆盖了其虚方法表中的方法

类B继承了类C的虚方法并添加了自己的虚方法到虚方法表中,当类B中重写了method1方法,就会把自己的虚方法表中的method1方法进行覆盖

方法重写的注意事项

方法重写本质上是重写虚方法表中的方法,如果一个方法不能添加到虚方法表中,则不能被重写

练习

  • 继承架构分析
package com.an.a;
class Dog{
    public void eat(){
        System.out.println("我要开始吃狗粮了");
    }
    public void drink(){
        System.out.println("我要开始喝水了哦");
    }
    public void careHome(){
        System.out.println("我还可以看家哦");
    }
}
class Hasaki extends Dog{//
    public void breakHome(){//哈士奇满足其需求不需要重写
        System.out.println("我要拆家了");
    }



}

class Shapi extends Dog{
    //对干饭行为的重写
    @Override
    public void eat (){
        System.out.println("我可以吃狗粮也可以吃骨头哦");
    }
}
class Tianyuan extends Dog {
    //对吃饭行为的重写
    @Override
    public void eat (){
        System.out.println("我喜欢吃剩饭哦");
    }
}

public class TestX {
    public static void main(String[] args) {
        Tianyuan ti = new Tianyuan();
        Hasaki ha = new Hasaki();
        Shapi sh = new Shapi();
        sh.eat();
        ha.eat();
        ti.eat();
    }
}

继承中的构造方法和this super关键字

继承中构造方法的访问特点


假设构造方法可以被继承,则会出现构造方法和类名不一样的情况

对于在子类构造方法中默认调用父类空参构造的理解(且必须放在第一行):从内层的角度分析,子类在创建对象的时候必须要使用父类的成员变量,而如果父类没有进行创建对象,则子类将无法完成创建对象的操作

对于this和super的使用汇总

this的另外一个用法--设置默认参数


由于this()和super()都需要放在构造方法的第一行,所以super和this不能同时出现在构造方法中

练习

package com.an.a;
class Employ{
    private String id;
    private String name;
    private double money;

    public Employ() {
    }

    public Employ(String id, String name, double money) {

        this.id = id;
        this.name = name;
        this.money = money;
    }

    public String getId() {
        return id;
    }

    public String getName() {
        return name;
    }

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

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

    public void setMoney(double money) {
        this.money = money;
    }

    public double getMoney() {
        return money;
    }

    public void work(){
        System.out.println("员工在工作");
    }
    public void eat(){
        System.out.println("吃米饭");
    }

}
class Manager extends Employ{
   private double bonus;

    public Manager() {
       
    }

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

    public double getBonus() {
        return bonus;
    }

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

    @Override
    public void eat(){
        System.out.println("管理他人");
    }
}
class Cooker extends Employ{
    public Cooker() {
    }

    public Cooker(String id, String name, double money) {
        super(id, name, money);
    }

    @Override
    public void eat (){
        System.out.println("我的工作是炒菜");
    }
}
public class TestX {
    public static void main(String[] args) {

    }
}

认识多态





对于系统的登录来初步认识多态

package com.ao.Deom;

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

    public Person() {
    }

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

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public void show() {
        System.out.println("姓名:"+this.name+"年龄:"+this.age);
    }
}

package com.ao.Deom;

public class Student extends Person {
    @Override
   public void show(){
       System.out.println("学生信息:姓名:"+super.getName()+"年龄:"+super.getAge());
   }
}
package com.ao.Deom;

public class Teacher extends Person {
    @Override
    public void show(){
        System.out.println("老师信息:姓名 :"+super.getName()+"年龄:"+super.getAge());
    }
}

package com.ao.Deom;

public class Adiministor extends Person{
    public void show(){
        System.out.println("管理员信息:姓名:"+super.getName()+"年龄:"+super.getAge());
    }
}

package com.ao.Deom;

public class Test {
    public static void main(String[] args) {
        Student s = new Student();
        s.setAge(23);
        s.setName("小刘");
        Teacher t = new Teacher();
        t.setAge(34);
        t.setName("小张");
        Adiministor a = new Adiministor();
        a.setAge(23);
        a.setName("小涛");
        register(s);
        register(t);
        register(a);
        Person per = new Person();
        per.show();
    }
    //将参数设置为这三个类的父类,则可以传递其所有子类的对象
    public static void register(Person per){
        per.show();
    }
}


多态中调用成员的特点


多态调用成员的内层分析

多态的优势和弊端

关于解耦合:当我们现在不是学生工作而是老师工作,我们只需要将new Student()修改成new Teacher()而不需要修改后面的方法调用程序

类型转化的弊端:不能调用子类特有的功能,在调用成员方法的时候,编译开左边运行看右边,那么在编译的时候会先检查左边父类有没有这个方法
解决方案:将类型转回子类即可

细节:在类型转化的时候不能瞎转

jdk14新特性将判断是否属于该类和强转合二为一

练习题目详情

对于饲养动物,我们发现饲养员可以饲养多种动物,即行为方法相同但是传递的参数类型不同,该题使用了方法的重载来实现这个功能。但是可以将传递的动物类型修改成这些动物的功能父类Animal,通过动态用一个函数即可完成

包和final


用完整的类名去创建对象其实是下面的这种写法

为了简化类名的书写引入了导包技术



在使用一个类的时候,会默认到相同的包中寻找

final


final的应用场景:用final修饰的方法一般表示一种规则,并且该规则不希望别人去改变

package com.ao.Deom;

import com.cd.cc.Goods;

public class Test {
    public static void main(String[] args) {
       /*
       1.修饰方法:表示该方法为最终方法,不能被重写
       2.修饰类:表示该类为最终类,不能被继承
       3.修饰变量表示该变量为常量,不能被改变
        */
         final int a=3;
        //a=4;改变将会报错
        System.out.println(a);
    }
}
 final class Fu{//final修饰继承将会报错
    public final   void show(){//final重写将会报错
        System.out.println("Fu show方法");
    }
}
class Zi extends Fu{
    @Override
    public void show(){
        System.out.println("Zi的show方法");
    }
}

常量的细节问题

package com.ao.Deom;

import com.cd.cc.Goods;

public class Test {
    public static void main(String[] args) {
        /*
        final 修饰的基本数据类型:记录的值不能发生改变
        final修饰的引用数据类型:记录的地址值不能发生改变,内部的属性值还是可以改变的
         */
        final double PI = 3.14;
        //PI = 3;常量记录的值不能发生改变将会报错
        final Person P = new Person("张三",34);
        //P = new Person();地址值不能发生改变将会报错
        P.setName("小张");
        P.setAge(23);
        //其内部的属性值将可以发生改变
    }
}

字符串不可变原理


字符串底层是使用字节数组来储存的,用final修饰表示字符数组的地址不可变,但是我们知道引用数据类型地址不可变但是其内容可变,但是字节数组用private修饰并且对外没有提供对应的get和set方法,所有外界无法修改字符串的属性值,以至于造成了字符串内容不可变

idea快捷键:ctrl+shif+u经小写变成大写




作用:代码的可读性增强了

权限修饰符



如ArrayList集合中的grow方法为多个方法的共性内容,被定义为private

代码块


根据代码块出现的位置不同,可以有下面的分类


局部代码块

局部指的是方法中,局部代码块指的是写在方法中的代码块


局部代码块的作用为提前结束变量的生命周期,起到节约内层的作用,主要用于早期计算机内层不足的情况下,现在已经很少使用了

package com.ao.Deom;

public class CodeBlock {
    public static void main(String[] args) {
        {
            int a=10;
        }//当代码执行到这里的时候,变量a已经从内层中消失了

        System.out.println(a);//此处将会报错
    }
}

构造代码块

构造代码块优先于构造方法执行,

package com.ao.Deom;

public class CodeBlock {
    public String name;
    public int age;
    {
        System.out.println("我要开始执行了");
    }
    public CodeBlock(){
        //System.out.println("我要开始执行了");
        System.out.println("空参构造");
    }
    public CodeBlock(String name,int age){
        //System.out.println("我要开始执行了");
        System.out.println("有参构造");
        this.age=age;
        this.name=name;
    }
    public static void main(String[] args) {
        CodeBlock c1 = new CodeBlock();
        CodeBlock c2 = new CodeBlock("张三",23);

    }
}


构造代码块:1.写在成员位置的代码块2.作用:把多个构造方法中的重复的代码抽取出来写成构造代码块3.执行时机:我们创建该类对象的时候会先执行构造代码块然后执行构造方法

但是对于目前来说这种方式已经慢慢被淘汰了,因为设置构造代码块,所有类型的构造方法在执行之前都会执行构造代码块中的内容,但是现在来说并不是所有的构造方法在执行之前都要执行构造代码块中的操作


对于上面操作的改进有2种方案:1.可以将重复的代码写在一个构造方法当中,当哪个构造方法需要执行,直接调用就可以,2.可以将重复的代码抽取成一个方法,当哪个构造方法需要直接调用方法即可

静态代码块

  • 静态代码块的执行时机
package com.ao.Deom;
/*
静态代码块的执行时机:
随着类的加载而执行,并且只会执行一次
 */
public class CodeBlock {
    public String name;
    public int age;
    static {
        System.out.println("静态代码块执行了");
    }
    {
        System.out.println("我要开始执行了");
    }
    public CodeBlock(){
        //System.out.println("我要开始执行了");
        System.out.println("空参构造");
    }
    public CodeBlock(String name,int age){
        //System.out.println("我要开始执行了");
        System.out.println("有参构造");
        this.age=age;
        this.name=name;
    }
    public static void main(String[] args) {
        CodeBlock c1 = new CodeBlock();
        CodeBlock c2 = new CodeBlock("张三",23);

    }
}

静态代码块的应用场景:可以在程序刚开始的时候时候做一些数据的初始化操作(有运用)

抽象类和抽象方法

如果将相同的行为和属性抽取成一个类,如果该类的行为不确定,可以将这个方法写成抽象方法,这样就可以不写方法体
抽象方法


注意事项

对于抽象类不能实例化对象的理解:1.抽象类高度抽象,其创建对象感觉不伦不类2.抽象类的有些方法可能为抽象方法没有方法体,如果抽象类创建了对象,然后调用抽象方法,那该怎么办
关于抽象类有构造方法:因为抽象方法可以被继承,而子类创建对象需要父类里面的属性,所以抽象类需要有构造方法


package com.cn.cc;

public class Testc {
//感想:当父类里面的方法的方法体不确定的时候,将方法设计成抽象方法,将类设计成抽象类
}
abstract class Animal{
    private String name;
    private int age;

    public Animal() {
    }

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //喝水
    public void drinkwater(){
        System.out.println("喝水");
    }
    //吃
     public abstract void eat ();
}
class frog extends Animal{

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

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

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

抽象类和抽象方法的意义

因为子类要重写父类的抽象方法,可以做到子类的相同行为的方法的格式相同,更利于团队合作

接口

对于接口的存在理解:当我们在写继承体系结构的时候,当有一种行为只存在子类中>=2个但是<所有子类所拥有时,在这种情况下无法将该方法抽取到父类中,但是如果写在子类中
当团队协作的时候我们无法保证我们的命名标准相同,对此我们可以将这个方法定义成一个接口


接口是一种规则,是对行为的抽象


package com.cn.cc;

import java.sql.SQLOutput;

public class Testc {
//感想:当父类里面的方法的方法体不确定的时候,将方法设计成抽象方法,将类设计成抽象类
}
interface Swim{
    public void swim();//定义游泳规则
}
abstract class Animal{
    private String name;
    private int age;
    //吃
    public abstract void eat();

}

class Flog extends Animal implements Swim{

    @Override
    public void eat() {
        System.out.println("青蛙吃虫子");
    }

    @Override
    public void swim() {
        System.out.println("蛙泳" +
                "");
    }
}
class Robit extends Animal{

    @Override
    public void eat() {
        System.out.println("兔子吃胡萝卜");
    }
}
class Dog extends Animal implements Swim{

    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }

    @Override
    public void swim() {
        System.out.println("狗刨");
    }
}

成员的特点和接口的各种关系

接口中成员的特点

对于子类中的公有属性会抽取到抽象父类中(或父类),所有成员的变量只有常量

接口和类之间的关系

细节:不同接口的相同方法的实现

interface Inter1{
    public abstract void mothod1();
    public abstract void mothod2();
    public abstract void mothod3();
}
interface Inter2{
    public abstract void mothod1();
    public abstract void mothod2();
    public abstract void mothod3();
    public abstract void mothod4();
}
class interfol implements Inter1,Inter2{

    @Override
    public void mothod1() {
        
    }

    @Override
    public void mothod2() {

    }

    @Override
    public void mothod3() {

    }

    @Override
    public void mothod4() {

    }
}

idea默认只实现一次

接口和接口之间是继承关系,如果实现类是实现了最下面的子接口,那么就需要重写这个体系的所有抽象方法

接口和抽象类的综合练习


错误的设计

这样设计的话乒乓球运动员和篮球教练 篮球运动员和乒乓球教练这两两来说没有什么关系,那为什么把他们都继承自同一个子类Person呢

第一种设计思路

第二种设计思路


第二种思路的具体实现

package com.cn.cc;

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

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

    public Person() {
    }

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

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
//运动员
abstract class Sporter extends Person{
   public abstract void study();
}
//教练
abstract class Coach extends Person{
    public abstract void teach();
}
class PingPangSporter extends Sporter implements English{

    @Override
    public void study() {
        System.out.println("乒乓球运动员学习打乒乓球");
    }

    @Override
    public void spackEnglish() {
        System.out.println("乒乓球运动员会说英语");
    }
}
class BasketBallSporter extends Sporter{

    @Override
    public void study() {
        System.out.println("篮球运动员学习打篮球");
    }
}
class PingPangCoach extends Coach implements English{

    @Override
    public void teach() {
        System.out.println("乒乒球教练会教打篮球");
    }

    @Override
    public void spackEnglish() {
        System.out.println("乒乒球教练会说英语");
    }
}
class BasketBallCoach extends Coach{

    @Override
    public void teach() {
        System.out.println("篮球教练会教打篮球");
    }
}
interface English{
    public void spackEnglish();
}

接口进阶

jdk8开始接口新增方法


出现的原因

当接口中的规则发生变化,如果实现类不进行更改,代码会立即报错



接口中有方法体的方法是在接口升级时为了兼容性而出现的


细节:当2个接口中有相同的默认方法,则这个默认方法必须被强制重写。因为如果2个默认方法没有被重写,在实现类中调用这个默认 方法,则虚拟机就不知道该调用哪个默认方法重写之后将会调用重写后的默认方法

如果static和defalt省略了,java会把方法当成是抽象方法进行处理
接口中的静态方法不能被重写,即使在子类中定义了和接口中一样的静态方法,这只是刚好接口中的方法和实现子类中的方法重名了

jdk9新增的方法


对于程序中出现了多次的代码,我们以前的做法是把他抽取成一个方法,然后在方法中调用

但是这样就出现了一个问题,因为这个抽取的方法我们是为本类中的方法服务的,这个方法往往不能构成我们需要的功能,所有这个方法我们不希望外界去访问


私有方法分为2种 一种是普通的private方法(不加defalt修饰)一种是加static修饰的


一种是为普通方法服务,一种是为静态方法服务的


接口的应用

1.如果想让某些javaBean类拥有一些规则,那么可以让这些类实现这些接口

2.如果某些方法的参数是接口,那么可以传递接口的实现类对象,形成多态

适配器设计模式


适配器设计的由来

package com.cn.cc;
//测试适配器设计模式
public class test {
}

interface inter1{
    public abstract void show1();
    public abstract void show2();
    public abstract void show3();
    public abstract void show4();
    public abstract void show5();
}
//当我们在一个类中只需要使用show1方法,但是由于接口的语法规定要重写接口的所有抽象方法
class intermol implements inter1{

    @Override
    public void show1() {
        
    }

    @Override
    public void show2() {

    }

    @Override
    public void show3() {

    }

    @Override
    public void show4() {

    }

    @Override
    public void show5() {

    }
}

对此我们可以在实现类和接口中间设计一个类作为适配器,在这个类中重写接口中的抽象方法,但是只重写不实现,然后让我们的子类继承适配器,然后调用它需要的方法

适配器设计模式代码

package com.cn.cc;
//测试适配器设计模式
public class test {
}

interface inter1{
    public abstract void show1();
    public abstract void show2();
    public abstract void show3();
    public abstract void show4();
    public abstract void show5();
}
//设计适配器类
//该适配器没有意义将其抽象化
abstract class  InterAdapter implements inter1{

    @Override
    public void show1() {

    }

    @Override
    public void show2() {

    }

    @Override
    public void show3() {

    }

    @Override
    public void show4() {

    }

    @Override
    public void show5() {

    }
}
//实现类 只需要使用show5
class intermol extends InterAdapter{
//重写需要使用的方法
    @Override
    public void show5(){
        System.out.println("show5方法");
    }

}

初识内部类


发动机是依赖车而存在的,如果将发动机的类定义在外面则没有什么意义

package com.cn.cc;

public class Test12 {
    /*
    需求:写一个javaBean类描述汽车
    属性:汽车的品牌 车龄 颜色 发动机的品牌 使用年限
    内部类访问特点:内部类可以直接访问外部类的成员 包括私有
    外部类要访问内部类的成员,必须创建对象
     */
}
class Car {
    String carName;
    int carAge;
    String carColor;
    public void show(){
        Engine e = new Engine();
        System.out.println(e.engineAge);//创建了内部类的对象,用对象调用将不会报错
        // System.out.println(engineName);//直接调用内部类的成员将会报错
    }
    //发动机为一个整体的对象,并且依附汽车而存在需要将其设计成一个类
    class Engine{
        public void show(){
            System.out.println(carAge);
        }
        String engineName;
        int engineAge;
    }
}

从底层来理解外部类和内部类之间的调用规则:所有的成员方法在调用的时候,都默认传入了类名 调用者的地址值1.从外部类方法中调用内部类的成员,方法传入了外部类对象调用者的地址,但是没有内部类的地址,所有无法调用2.内部类可以任意调用外部类的成员,因为内部类的对象先创建外部类的对象,然后才能创建内部类的对象,所以内部类中调用外部类的成员已经知道外部类对象的地址

内部类在实际中的应用

迭代器既属于集合,但是又独立于集合,将其设计成ArrayList的一个内部类

成员内部类




只有是可以用来修饰成员的都可以用来修饰成员内部类


成员内部类不包含用static修饰的

获取成员内部类对象

package com.cn.cc;

public class Test12 {
    /*
    获取内部类对象的2种方式
    1.外部类中编写方法,对外提供内部类的对象
    2.直接创建:
    格式:外部类名.内部类名 对象名= 外部对象.内部对象
    举例:Outer.Inner oi = new Outer().new Inner()
     */
    public static void main(String[] args) {
        Outer.Inner io = new Outer().new Inner();//直接创建内部类对象
`  //对于右边的理解:new Outer()创建外部类对象然后去调用其内部类成员的构造方法`
        Outer o = new Outer();
       Outer.Inner instance = o.getInstance();//获取内部类对象的第二种方式
    }

}
class Outer{
    String name;
    int age;
    public Inner getInstance(){//获取内部类的对象


        return      new Inner();

    }
    class Inner{
        public void show(){
            System.out.println("show");
        }
    }
}

在java源码中的应用

源码中设计了一个成员内部类用private修饰,在外部无法创建其的对象,java在其外部类中设计了一个方法用于返回内部类的对象

成员内部类如何获取外部类的成员变量

package com.cn.cc;

public class Test12 {

    public static void main(String[] args) {
        Outer.Inner io = new Outer().new Inner();
        io.show();
    }

}
class Outer{
   private int a = 10;
    class Inner{
      private int a = 20;
        public void show (){
            int a = 30;
            //对于访问也是符合就近原则
            System.out.println(a);//先从局部变量找起 30
           System.out.println(this.a);//20本类成员位置没有该变量不能往后寻找  就会报错
            System.out.println(Outer.this.a);//10
        }
    }
}

对内部类的内层分析:理解System.out.println(Outer.this.a)

通过内存图分析:在测试类中创建内部类的对象的时候,同时会记录外部类的地址Outer.this

静态内部类


package com.cn.cc;

public class Test12 {

    public static void main(String[] args) {
/*
注意事项:
1.静态内部类也是成员内部类的一种
2.静态内部类只能访问外部类的静态方法和静态变量
如果要想访问外部类的非静态属性和方法需要创建外部类的对象
创建静态内部类的格式:
    外部类名.内部类名 对象名 = new 外部类名.内部类名();
调用静态方法的格式:
外部类名.内部类名.方法名();
 */
        //1.创建静态内部类对象
        Outer.Inner io = new Outer.Inner();
        io.show1();//非静态方法被调用了
       Outer.Inner.show2();//调用静态方法   静态方法被调用了
    }

}
class Outer {
    public int a;
    static class Inner{
        public void show1 (){
            System.out.println("非静态方法被调用了");
        }
        public static void show2(){
            System.out.println("静态方法被调用了");
        }
    }
}

局部内部类


局部内部类类似于方法里面的局部变量,可以用于修饰局部变量的都可以用来修饰局部内部类

在外界无法直接使用类里面方法里面的局部变量,就易于理解在外部无法直接使用show方法里面的局部变量a

这些内部类用处不多,了解即可

posted @ 2023-01-20 11:00  一往而深,  阅读(50)  评论(0编辑  收藏  举报