08面向对象

面向对象

  • 面向过程思想

    • 步骤清晰简单,第一步,第二步

    • 面向过程适合处理简单问题

  • 面向对象思想

    • 物以类聚,分类思想模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后才对某和分类下的细节进行面向对象过程的思索
    • 面向对象适合处理复杂的问题,适合处理需要多人协作的问题
  • 对于复杂的的事物,为了从宏观上把握、从整体上合理分析,我们需使用面向对象的思路来分析系统。但是,具体到微观操作,仍然需要面向过程的思路处理

  • 面向对象编程的本质是:以类的方式组织代码,以对象的组织(封装)数据

  • 抽象

  • 三大特征

    • 封装
    • 继承
    • 多态
  • 从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象

  • 从代码运行角度考虑是先有类后有对象。类是对象的模板

方法回顾和加深

  • 方法的定义
    • 修饰符
    • 返回类型
    • break:跳出switch循环,结束循环 return: 结束语句后面的也不执行
    • 方法名 驼峰命名法 见名知意
    • 参数列表 (参数类型,参数名)…
    • 异常抛出 throws catch //数组下标越界
//Demo01类
public class Demo01 {
    //main方法
    public static void main(String[] args) {

    }
    /*
    修饰符  返回值类型 方法名(...){
        //方法体
        return 返回值;
    }
     */
    public String sagHello(){
        return "hello";
    }
    public int max(int a,int b){
        return a>b?a:b;//三元运算符
    }

}
  • 方法的调用:递归

    • 静态方法 调用 类名.方法名 类存在就已经存在了

      public class Demo02 {
          public static void main(String[] args) {
              student.say();
          }
      }
      //学生类
      public class Student {
          //非静态方法
          public static void say(){
              System.out.println("啊是为了克服·");
          }
      }
      
    • 非静态方法 调用 先实列化 Student student = new Student();需要实例化之后才存在

      public class Demo02 {
          public static void main(String[] args) {
              //实例化这个类   new
              //对象类型  对象名= 对象值;
              Student student =new Student();
              student.say();
          }
      }
      //学生类
      public class Student {
          //非静态方法
          public  void say(){
              System.out.println("啊是为了克服·");
          }
      }
      

      当任意定义两个方法时,两个方法都为静态可互相调用,

      若不都是静态不可用,因为静态方法是随着类出现就已经出现了,非静态类需要实例化之后才存在

      public class Demo03 {
          public static void main(String[] args) {
             //调用的是动态方法
              Demo03 demo03=new Demo03();
              demo03.add(1,3);
              //调用的是静态方法
              int add1 = Demo03.add1(1,3);
          }
          public int add(int a ,int b){
              return a+b;
          }
          public static int add1(int a ,int b){
              return a+b;
          }
      }
      
    • 形参和实参

      形参可以随意定义类型,传入的实参和形参需要保持类型一致

    • 值传递和引用传递

      值传递,此a非彼a,传进去的是个形式参数没有返回值,他还是原来的那个a。传入方法的a变成了经过方法之后的a

      引用传递传递对象:可以用值传递改变引用传递中的对象//这里不会了去看b站遇见狂神说java基础的62课,特别清楚

    • this关键字

对象的创建分析

  • 类是一种抽象的数据类型,它是对某一事物整体描述/定义,但是并不能代表某一个具体的事物
  • 对象是抽象概念的具体实例
    • 能够体现出特点,展现出功能的是具体的实例,而不是一个抽象的概念
  1. 使用new 关键字创建对象
  2. 使用new关键字创建时,除了分配内存空间外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
  3. 类中的构造器也称构造方法,是在进行创建对象的时候必须要调用的。并且构造器有其特点
    • 必须和类的名字相同
    • 必须没有返回类型,也不能写void
//class文件里默认添加了一个没有返回值的方法
public class Person {
    //一个类即使什么都不写也会存在一个构造器
    /**
     * //用处1.构造对象实例化初始值
     * //核心作用 使用new关键字必须要有构造器
     * public class Person {
     * //方法名和类名相同,无参构造,
     *     public Person() {
     *     this.name=“阿娆”
            }
     //有参构造:一旦定义了有参构造,无参构造就必须显示定义
     public Person(String name){
        this.name=name;
     }
     * }
     */
    String name;
    //1.使用new关键字,本质实在调用构造器
    //2.用来初始化值
    public Person(){
       this.name="阿娆1"
    }
    //有参构造:一旦定义了有参构造,无参构造就必须显示定义,重载
    public Person(String name){
        this.name=name;
    }
    //alt+inset我的用 不了

}
测试
public class Demo04 {
    public static void main(String[] args) {
        //new Person();
        //new 实例化了一个对象
        Person person = new Person("阿娆");//带入参数,进入 public Person(String name)不带参数进入public Person()
        System.out.println(person.name);
    }
}

注意点

定义了有参构造,无参就必须显示定义

作用

1.使用new关键字,本质实在调用构造器
2.用来初始化值

内存

狂神面向对象06

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Uq978IYn-1636553536731)(D:\阿娆要努力呀\图片都在这\08.jpg)]

突然想起来这是在我的电脑上的位置,有点点尴尬,但是图已经无了

//突出起来的总结
1.类与对象
    类是一个模板,对象是一个具体的实例
2.方法
    定义、调用
3.对应的引用
    引用类型:基本类(8)
    对象是通过引用来操作的:栈---->堆()
4.属性:字段Field成员变量
    默认初始化:
        数字 0   0.0
        char  u0000
        boolen   false
        引用:null
    修饰符 属性类型 属性名=属性值!
5.对象的创建和使用
    必须使用new 关键字创建对象,构造器  Person  某个名字 = new Person
    对象的属性   某个名字.name/age....
    对象的方法   某个名字.short()
6.类:
    静态的属性       属性
    动态的行为       方法

面向对象的三大特征

封装

属性私有private get/set 多用于属性,很少用于方法

public class Student {
    //属性私有
    private String name;
    private int age;
    private char sex;
    //因为私有了所以不能使用new 创建对象了需要提供一些可以操作这些属性的方法
    //提供一下public 的get/set方法
    //get
    public String getName(){
        return this.name;
    }
    //提供一个public 的set
    public void setName(String name){
        this.name=name;
    }
    public int getAge() { 
        return age;
    }
    public void setAge(int age) {
        if (age>100||age<0){
            this.age = 1;
        }else {
            this.age = age;
            }
    }
    ......//另外1个没生成
}//alt+insert快捷键或者找到菜单栏code找到Generat选择get/set会自动生产
测试
public class Demo02 {
    public static void main(String[] args) {
        //实例化这个类   new
        //对象类型  对象名= 对象值;
        Student student =new Student();
        Student s1= new Student();
        s1.setName("阿娆");//设置
        s1.getName();//得到
        System.out.println(s1.getName());
        s1.setAge(999);//不合法会返回设定的1
        System.out.println(s1.getAge());
    }
}

//封装的好处,可以在系统中改内容

意义

  1. 提高程序的安全性,保护数据
  2. 隐藏代码的实现细节
  3. 统一接口 get/set
  4. 系统可维护性强
继承
  1. 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模

  2. extends的意思是“扩展”,子类是父类的扩展。

  3. java中类只有单继承,没有多继承!//一个父类可以有多个子类,子类只有一i个父类

  4. 继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等、

  5. 继承关系的两个类,一个为子类(派生类)、一个为父类(基类)。子类继承父类,使用关键字extends来表示。

  6. 子类和父类之间,从意义上讲应该是具有“is a”的关系

    //class文件里默认添加了一个没有返回值的方法
    //person   人
    //在java中默认直接或者间接继承object类
    public class Person {
        //public 可用
        //private  不可用
        //default  默认的
        //protected 受保护的
        public int money=10000;//public 可继承若换成private 则Deom07报错
        public void say(){
            System.out.println("ds1f2fs0dv");
        }
        //或者使用get/set来调用方法
    
        public int getMoney() {
            return money;
        }
    
        public void setMoney(int money) {
            this.money = money;
        }
    }
    public class Demo07 {
        public static void main(String[] args) {
    
            Student student = new Student();//new一个学生对象,
            student.say();//学生继承了人的方法
            System.out.println(student.money);
            student.getMoney();
            //Ctrl + h 可调出继承树
        }
    }
    //输出
    阿娆
    阿娆好可爱
    阿娆在摸鱼
    
  7. object类 //所有的都直接或间接继承object类

  8. super/this

    或者使用get/set来产生构造器,然后实现继承

    ***调用父类的构造器super.()必须在子类构造器的第一行

    public class Person {
        protected void print(){
            System.out.println("djskvzfc");
        }
    }
    //学生 is  人
    public class Student extends Person{
        public void print(){
            System.out.println("jdsf");
        }
        public void test1() {
            print();//Student
            this.print();//Student
            super.print();//父类的name
        }
    }
    public class Demo07 {
        public static void main(String[] args) {
            Student student = new Student();
            student.test1();
        }
    }
    jdsf
    jdsf
    djskvzfc
    

    ***Super***注意点

    1. super调用父类的构造方法,必须在构造方法的第一行
    2. super必须只能出现在总类的方法或者构造方法中!
    3. super和this 不能同时调用构造方法!

    VS this

    ​ 代表的对象不同

    ​ this:本身调用这个对象

    ​ super:代表父类对象的应用

    ​ 前提

    ​ this :没有继承也可以使用

    ​ super:只能在继承条件才可以使用

    ​ 构造方法

    ​ this():本类的构造

    ​ super():父类的构造!

  9. 方法重写与重载不同

    public class A extends B {
        public static void text(){
            System.out.println("A=>text()");
        }
    }
    public class B {
        public static void text(){
            System.out.println("B=>text()");
        }
    }
    public class Demo07 {
        public static void main(String[] args) {
            //方法的调用至于左边有关,定义的数据类型有关
            A a =new A();
            a.text();
            //父类的引用指向子类
            B b = new A();
            b.text();
        }
    }
    A=>text()
    B=>text()
    

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kJAtJfAu-1636553536734)(C:\Users\17730\AppData\Roaming\Typora\typora-user-images\image-20211108113443848.png)]去掉static会出现这个

//使用快捷键Ctrl+o 或者code下找到Generate 可自动重写

public class A extends B {
    @Override//注解:重写
    public void text() {
        
    }
}
public class A extends B {
    @Override
    public void text() {
        System.out.println("A=>text()");
    }
}
public class B {
    public void text(){
        System.out.println("B=>text()");
    }
}
public class Demo07 {
    public static void main(String[] args) {
        //方法的调用至于左边有关,定义的数据类型有关
        A a =new A();
        a.text();
        //父类的引用指向子类
        B b = new A();
        b.text();
    }
}
A=>text()
A=>text()

静态的方法和非静态的方法区别很大

静态方法只能被继承不能被重写

静态成员(方法或属性)是类的成员存放在栈中,类可以直接调用(是属于类的静态成员,当然对象也可以调用,只是说你可以使用而已);实例成员是对象的成员,存放在堆中,只能被对象调用。
重写的目的在于根据创造对象的所属类型不同而表现出多态。因为静态方法无需创建对象即可使用。没有对象,重写所需要的“对象所属类型” 这一要素不存在,因此无法被重写。

重写 需要有继承关系,子类重写父类的方法

	1. 方法名必须相同
	1. 参数列表必须相同
	1. 修饰符:范围可以扩大但是不能缩小:         public>protected>default>private
	1. 抛出异常:范围,可以被缩小,但不能扩大:   ClassNotFoundException-->Exception(大)

重写,子类的方法和父类必须一致:方法体不同

为什么要重写

  1. 父类的功能,子类不一定需要,或者不一定满足!
  2. Alt+Insert或者使用快捷键Ctrl+o 或者code下找到Generate 可自动重写
多态
  • 动态编译:可以是使类型的可扩展性增强

    import com.sun.org.apache.xpath.internal.objects.XObject;
    
    public class Demo01 {
        public static void main(String[] args) {
            //一个对象的实际类型是确定的
            //new Student();       new 一个Student类
            //new Person();     new 一个person类
            //可以指向的引用类型就不一定  :   父类的引用指向子类
            //Student中的方法来自自己和从父类继承来的
            Student s1=new Student();
            //Person 父类型,可以指向子类,但是不能调用子类独有的方法
            Person  s2=new Student();
            Object  s3=new Student();
            s1.run();
            s2.run();//子类重写的父类的执行子类的
            s1.eat();
            //s2.eat();     错误      因为左边的Person中没有eat()方法
    
        }
    }
    public class Person {
        public void run(){
            System.out.println("run");
        }
    }
    public class Student extends Person {
        @Override
        public void run() {
            System.out.println("son");
        }
        public void eat(){
            System.out.println("eat");
        }
    }
    

    多态的注意事项

    1. 多态是方法的多态,属性没有多态
    2. 父类和子类,有联系 类型转换异常 ClassCastException
    3. 存在条件:继承关系,方法需要重写,父类的引用指向子类对象 father f1 =new Son()
    4. 不能被重写的
      • static方法属于类,不属于实例
      • final 常量无法改变
      • private 方法 私有的
  • 既同一方法可以根据发送对象的不同而采用多种不同的行为方式

  • 一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多

  • 多态存在的条件

    • 有继承关系
    • 子类重写父类方法
    • 父类引用指向子类对象
  • 注意:多态是方法的多态,属性没有多态

  • instanceof

    public class Demo02 {
        public static void main(String[] args) {
           // Object>Person> Student=Teacher
            Object object= new Student();
            System.out.println(object instanceof Student);//TRUE
            System.out.println(object instanceof Person);//TRUE
            System.out.println(object instanceof Object);//TRUE
            System.out.println(object instanceof Teacher);//FALSE
            System.out.println(object instanceof String);//FALSE
            Person person= new Student();
            System.out.println(person instanceof Student);//TRUE
            System.out.println(person instanceof Person);//TRUE
            System.out.println(person instanceof Object);//TRUE
            System.out.println(person instanceof Teacher);//FALSE
           // System.out.println(person instanceof String);//报错
            Student student= new Student();
            System.out.println(student instanceof Student);//TRUE
            System.out.println(student instanceof Person);//TRUE
            System.out.println(student instanceof Object);//TRUE
            //System.out.println(student instanceof Teacher);//报错
            //System.out.println(student instanceof String);//报错
        }
    }
    

多态

public class Demo02 {
    public static void main(String[] args) {
    //类型之间的转换           父     子
    //数据类型        高      低
    Person student = new Student();
    //student 将这个对象转换为Student类型,就可调用Student中的方法了     高-》低
        Student student1 =(Student)student;//将Person的Student强制转换成了Student类型
        student1.run();//可以调用Student中的方法了
        //子类转换为父类有可能丢失方法
    }
}
1.父类引用指向子类,
2.把子类转换为父类是向上转
3.父转子向下转,会丢失方法类似与数据类型丢失精度
4.方便方法调用

static

public class Student {
    private static int age;//静态变量
    private double score;//非静态变量
    public void run(){}
    public static void go(){}
    public static void main(String[] args) {
        Student student = new Student();
        System.out.println(Student.age);
        System.out.println(student.score);
        System.out.println(student.age);
        //System.out.println(Student.score);//非静态方法
        go();
        //run();不能调用
        new Student().run();//需要new之后才行
    }
}

输出顺序

public class Student extends Person{
    {
        System.out.println("匿名代码块");
    }
    static {
        System.out.println("静态代码快");//只输出一次
    }
    public Student(){
        System.out.println("构造方法");
    }

    public static void main(String[] args) {
        Student student = new Student();
        System.out.println("**********");
        Student SI=new Student();
    }
}

输出是:

静态代码快
匿名代码块
构造方法


匿名代码块
构造方法

抽象类和接口

抽象
  1. abstract 修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法,如果修饰类,那就是就是抽象类
  2. 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类
  3. 抽象类,不能使用new关键字,但是有抽象方法的类一定要声明为抽象类
  4. 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的
  5. 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明抽象类

//abstract 抽象类:类     extends:单继承 (接口可以多继承)
public abstract class Action {
    //加上abstract抽象方法,只有方法名,没有方法的实现
    //约束~ 等继承他的子类来实现
    public abstract void something();
    //1.不能new这个抽象类,只能靠子类实现它,:约束
    //2.抽象类中可以写普通方法
    //3.抽象方法必须在抽象类中
}
package Demo02;
//抽象类的所有方法,继承了他的子类,都必须它的方法,除非子类也是抽象,则由子类的子类来实现
public class A extends Action{
 //必须重写Action中的方法
    public void something(){
    }
}

思考 : new 存在构造器么

即使你没有提供任何构造函数,编译器将为抽象类添加默认的无参数的构造函数,没有的话你的子类将无法编译,因为在任何构造函数中的第一条语句隐式调用super()。

  1. 有抽象方法的类一定是抽象类
  2. 是抽象的类不一定要有抽象方法

抽象类存在的意义 提高开发效率

接口
  1. 普通类:只有具体实现
  2. 抽象类:具体实现和规范(抽象方法)都有
  3. 接口:只有规范 自己无法写方法,只能做约束 面向接口编程
  4. 接口就是规范,定义的是一组规则,体现了现实世界中”如果你是…则必须能…“的思想,
  5. 接口的本质是契约,就像我们人间的法律一样,制定好后大家都遵守
  6. oo的精髓,是对对象的抽象,最能体现的就是接口,合理的去抽象

声明类的关键字是class,声明接口的关键字是interface

默认是public abstract

不能定义方法,只能实现接口

接口需要实现类(命名方法是后面加上impl implements)

实现了接口的类,就需要重写接口中的方法

package Demo03;
public interface UserService  {
    void add(String name);
    void delete(String name);
    void update(String name);
    void query(String name);
}
package Demo03;
public class UserServiceImpl implements UserService {
    @Override
    public void update(String name) {
    }
    @Override
    public void add(String name) {
    }
    @Override
    public void delete(String name) {
    }
    @Override
    public void query(String name) {
    }
}

在接口中定义常量默认 public static final

内部类

内部类就是在一个类的内部再定义一个类,比如,A类中定义一个人B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类

成员内部类

这个是最常见的内部类之一了,其定义也很简单,在一个类里面作为类的一个字段直接定义就可以了,例:

public class InnerClassTest {

public class InnerClassA {
    
}

在这里 InnerClassA 类为 InnerClassTest 类的普通内部类,在这种定义方式下,普通内部类对象依赖外部类对象而存在,即在创建一个普通内部类对象时首先需要创建其外部类对象,我们在创建上面代码中的 InnerClassA 对象时先要创建 InnerClassTest 对象,例:

public class InnerClassTest{
public int outField1 = 1;
protected int outField2 = 2;
int outField3 = 3;
private int outField4 = 4;

public InnerClassTest() {
    // 在外部类对象内部,直接通过 new InnerClass(); 创建内部类对象
    InnerClassA innerObj = new InnerClassA();
    System.out.println("创建 " + this.getClass().getSimpleName() + " 对象");
    System.out.println("其内部类的 field1 字段的值为: " + innerObj.field1);
    System.out.println("其内部类的 field2 字段的值为: " + innerObj.field2);
    System.out.println("其内部类的 field3 字段的值为: " + innerObj.field3);
    System.out.println("其内部类的 field4 字段的值为: " + innerObj.field4);
}

public class InnerClassA {
    public int field1 = 5;
    protected int field2 = 6;
    int field3 = 7;
    private int field4 = 8;
    //        static int field5 = 5; // 编译错误!普通内部类中不能定义 static 属性

    public InnerClassA() {
        System.out.println("创建 " + this.getClass().getSimpleName() + " 对象");
        System.out.println("其外部类的 outField1 字段的值为: " + outField1);
        System.out.println("其外部类的 outField2 字段的值为: " + outField2);
        System.out.println("其外部类的 outField3 字段的值为: " + outField3);
        System.out.println("其外部类的 outField4 字段的值为: " + outField4);
    }
}

public static void main(String[] args) {
    InnerClassTest outerObj = new InnerClassTest();
    // 不在外部类内部,使用:外部类对象. new 内部类构造器(); 的方式创建内部类对象
    //        InnerClassA innerObj = outerObj.new InnerClassA();
    }
}

这里的内部类就像外部类声明的一个属性字段一样,因此其的对象时依附于外部类对象而存在的

我们注意到,内部类对象可以访问外部类对象中所有访问权限的字段,同时,外部类对象也可以通过内部类的对象引用来访问内部类中定义的所有访问权限的字段,后面我们将从源码里面分析具体的原因。

静态内部类

一个类的静态成员独立于这个类的任何一个对象存在,只要在具有访问权限的地方,我们就可以通过 类名.静态成员名 的形式来访问这个静态成员,同样的,静态内部类也是作为一个外部类的静态成员而存在,创建一个类的静态内部类对象不需要依赖其外部类对象。例:

public class InnerClassTest {
	public int field1 = 1;
public InnerClassTest() {
	System.out.println("创建 " + this.getClass().getSimpleName() + " 对象");
    // 创建静态内部类对象
    StaticClass innerObj = new StaticClass();
    System.out.println("其内部类的 field1 字段的值为: " + innerObj.field1);
    System.out.println("其内部类的 field2 字段的值为: " + innerObj.field2);
    System.out.println("其内部类的 field3 字段的值为: " + innerObj.field3);
    System.out.println("其内部类的 field4 字段的值为: " + innerObj.field4);
}

static class StaticClass {
    public int field1 = 1;
    protected int field2 = 2;
    int field3 = 3;
    private int field4 = 4;
    // 静态内部类中可以定义 static 属性
    static int field5 = 5;

    public StaticClass() {
        System.out.println("创建 " + StaticClass.class.getSimpleName() + " 对象");
        //            System.out.println("其外部类的 field1 字段的值为: " + field1); // 编译错误!!
        }
    }

public static void main(String[] args) {
    // 无需依赖外部类对象,直接创建内部类对象
//        InnerClassTest.StaticClass staticClassObj = new InnerClassTest.StaticClass();
		InnerClassTest outerObj = new InnerClassTest();
    }
}

可以看到,静态内部类就像外部类的一个静态成员一样,创建其对象无需依赖外部类对象(访问一个类的静态成员也无需依赖这个类的对象,因为它是独立于所有类的对象的)。但是于此同时,静态内部类中也无法访问外部类的非静态成员,因为外部类的非静态成员是属于每一个外部类对象的,而本身静态内部类就是独立外部类对象存在的,所以静态内部类不能访问外部类的非静态成员,而外部类依然可以访问静态内部类对象的所有访问权限的成员,这一点和普通内部类无异。

匿名内部类

局部内部类

局部内部类使用的比较少,其声明在一个方法体 / 一段代码块的内部,而且不在定义类的定义域之内便无法使用,其提供的功能使用匿名内部类都可以实现,而本身匿名内部类可以写得比它更简洁,因此局部内部类用的比较少。来看一个局部内部类的小例子:

public class InnerClassTest {
public int field1 = 1;
protected int field2 = 2;
int field3 = 3;
private int field4 = 4;

public InnerClassTest() {
    System.out.println("创建 " + this.getClass().getSimpleName() + " 对象");
}

private void localInnerClassTest() {
    // 局部内部类 A,只能在当前方法中使用
    class A {
        // static int field = 1; // 编译错误!局部内部类中不能定义 static 字段
        public A() {
            System.out.println("创建 " + A.class.getSimpleName() + " 对象");
            System.out.println("其外部类的 field1 字段的值为: " + field1);
            System.out.println("其外部类的 field2 字段的值为: " + field2);
            System.out.println("其外部类的 field3 字段的值为: " + field3);
            System.out.println("其外部类的 field4 字段的值为: " + field4);
        }
    }
    A a = new A();
    if (true) {
        // 局部内部类 B,只能在当前代码块中使用
        class B {
            public B() {
                System.out.println("创建 " + B.class.getSimpleName() + " 对象");
                System.out.println("其外部类的 field1 字段的值为: " + field1);
                System.out.println("其外部类的 field2 字段的值为: " + field2);
                System.out.println("其外部类的 field3 字段的值为: " + field3);
                System.out.println("其外部类的 field4 字段的值为: " + field4);
            }
        }
        B b = new B();
    }
    //        B b1 = new B(); // 编译错误!不在类 B 的定义域内,找不到类 B,
    }

public static void main(String[] args) {
    InnerClassTest outObj = new InnerClassTest();
    outObj.localInnerClassTest();
}

同样的,在局部内部类里面可以访问外部类对象的所有访问权限的字段,而外部类却不能访问局部内部类中定义的字段,因为局部内部类的定义只在其特定的方法体 / 代码块中有效,一旦出了这个定义域,那么其定义就失效了,就像代码注释中描述的那样,即外部类不能获取局部内部类的对象,因而无法访问局部内部类的字段。

匿名内部类

匿名内部类有多种形式,其中最常见的一种形式莫过于在方法参数中新建一个接口对象 / 类对象,并且实现这个接口声明 / 类中原有的方法了:

public class InnerClassTest {
public int field1 = 1;
protected int field2 = 2;
int field3 = 3;
private int field4 = 4;

public InnerClassTest() {
    System.out.println("创建 " + this.getClass().getSimpleName() + " 对象");
}
// 自定义接口
interface OnClickListener {
    void onClick(Object obj);
}

private void anonymousClassTest() {
    // 在这个过程中会新建一个匿名内部类对象,
    // 这个匿名内部类实现了 OnClickListener 接口并重写 onClick 方法
    OnClickListener clickListener = new OnClickListener() {
        // 可以在内部类中定义属性,但是只能在当前内部类中使用,
        // 无法在外部类中使用,因为外部类无法获取当前匿名内部类的类名,
        // 也就无法创建匿名内部类的对象
        int field = 1;
        
        @Override
        public void onClick(Object obj) {
            System.out.println("对象 " + obj + " 被点击");
            System.out.println("其外部类的 field1 字段的值为: " + field1);
            System.out.println("其外部类的 field2 字段的值为: " + field2);
            System.out.println("其外部类的 field3 字段的值为: " + field3);
            System.out.println("其外部类的 field4 字段的值为: " + field4);
        }
    };
    // new Object() 过程会新建一个匿名内部类,继承于 Object 类,
    // 并重写了 toString() 方法
    clickListener.onClick(new Object() {
        @Override
        public String toString() {
            return "obj1";
        }
    });
}

public static void main(String[] args) {
    InnerClassTest outObj = new InnerClassTest();
    outObj.anonymousClassTest();
}

上面的代码中展示了常见的两种使用匿名内部类的情况:
1、直接 new 一个接口,并实现这个接口声明的方法,在这个过程其实会创建一个匿名内部类实现这个接口,并重写接口声明的方法,然后再创建一个这个匿名内部类的对象并赋值给前面的 OnClickListener 类型的引用;
2、new 一个已经存在的类 / 抽象类,并且选择性的实现这个类中的一个或者多个非 final 的方法,这个过程会创建一个匿名内部类对象继承对应的类 / 抽象类,并且重写对应的方法。

同样的,在匿名内部类中可以使用外部类的属性,但是外部类却不能使用匿名内部类中定义的属性,因为是匿名内部类,因此在外部类中无法获取这个类的类名,也就无法得到属性信息。

n("其外部类的 field2 字段的值为: " + field2);
System.out.println("其外部类的 field3 字段的值为: " + field3);
System.out.println("其外部类的 field4 字段的值为: " + field4);
}
};
// new Object() 过程会新建一个匿名内部类,继承于 Object 类,
// 并重写了 toString() 方法
clickListener.onClick(new Object() {
@Override
public String toString() {
return “obj1”;
}
});
}

public static void main(String[] args) {
    InnerClassTest outObj = new InnerClassTest();
    outObj.anonymousClassTest();
}

上面的代码中展示了常见的两种使用匿名内部类的情况:
1、直接 new 一个接口,并实现这个接口声明的方法,在这个过程其实会创建一个匿名内部类实现这个接口,并重写接口声明的方法,然后再创建一个这个匿名内部类的对象并赋值给前面的 OnClickListener 类型的引用;
2、new 一个已经存在的类 / 抽象类,并且选择性的实现这个类中的一个或者多个非 final 的方法,这个过程会创建一个匿名内部类对象继承对应的类 / 抽象类,并且重写对应的方法。

同样的,在匿名内部类中可以使用外部类的属性,但是外部类却不能使用匿名内部类中定义的属性,因为是匿名内部类,因此在外部类中无法获取这个类的类名,也就无法得到属性信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值