java基础-面向对象

面向对象笔记

一、 对象在内存中的存放方法以及被调用过程

  1. class文件首先被加载到方法区中的class文件内容区域,类的成员变量成员方法自然也随着类的加载而被加载到方法区。
  2. 创建对象的时候,首先在栈中开辟空间,用于存放对对象的引用地址(即 Phone p的地址);
  3. 在堆中开辟空间存放对象实体(即new Phone()),同时会给该对象的属性分配内存空间来存放该对象的属性值,即会把方法区中该类的成员变量复制一份到堆内存中(首先是默认值,然后才是调用者赋给的值),用于存放该对象的属性值,但该类的方法就不会被复制到堆中,而是通过堆中一个指针的引用指向方法区中该方法。(其实这一点很好理解,每个对象的属性值不一样,但是每个对象的行为却是一样的,因此每个对象需要有自己的成员变量来保存自己的属性值信息,而方法不用,只用调用同一个方法即可。)
  4. 在调用对象的成员变量时,实际上是给该成员变量进行复制,因此是在堆中执行的。
  5. 在调用对象的成员方法时,该方法会被加载到栈中(实际上在栈中有个方法栈专门用来保存对方法的引用),该方法被调用完成后,立刻就被销毁了,因此对成员方法的调用发生在栈内存中。

注意:栈的特点是先进后出,画图的时候因从下往上,这里只是为了方便解释,画图的时候从上往下。

  • 一个对象的引用图解


    一个对象的内存图解

  • 两个对象的内存图解


    两个对象的内存图解

    注意到:

    1. 两个对象分别有自己的成员变量空间;
    2. 两个对象通过地址指向方法区的同一个地址,即成员方法的地址;

  • 三个对象的内存图解

    三个对象的内存图解

    注意到:两个引用指向同一个对象时,实际上是对同一个对像进行操作

二、成员变量、局部变量、静态变量的区别

(一) 成员变量和局部变量的区别

  1. 在类中的位置不同
    • 成员变量:在类中方法外
    • 局部变量:在方法定义中或方法声明上
  2. 在内存中的位置不同
    • 成员变量:在堆内存中(对象的实体在堆内存中,成员变量是跟随着对象的实体,因此也就在堆内存中)
    • 局部变量:在栈内存中(局部变量在方法内部,方法在被调用的时候会被加载到栈中,因此局部变量存在于栈内存中)
  3. 生命周期不同(在内存中的位置不同,生命周期自然不同)
    • 成员变量:随着对象的创建而存在,随着对象的消失而消失;
    • 局部变量:随着方法的调用而存在,随着方法的调用完毕而消失;
  4. 初始化值不同(在内存中的位置不同,初始化值自然不同)
    • 成员变量:有默认初始值;
    • 局部变量:没有默认初始化值,必须定义、赋值,然后才能使用;

(二)成员变量和静态变量的区别

  1. 所属不同
    • 成员变量:属于对象,也叫实例变量(对象变量)
    • 静态变量:属于类,也叫类变量
  2. 内存中位置不同
    • 成员变量:随着对象的创建而存在于堆内存中;
    • 静态变量:随着类的加载而存在于方法区的静态区
  3. 生命周期不同
    • 成员变量:随着对象的创建而存在,随着对象的消失而消失;
    • 静态变量:随着的加载而加载,随着类的消失而消失;
  4. 调用不同
    • 成员变量:只能通过对象名来调用;
    • 静态变量:即可以通过类名来调用,也可以通过对象名来调用;

(三)静态变量和局部变量的区别

  • 这个区别太明显,不做区分;

注意事项:因为在内存中的位置不同,所以要注意形式参数问题

  1. 基本类型:形式参数的改变不影响实际参数
  2. 引用类型:形式参数的改变直接影响实际参数

三、构造方法

构造方法的注意事项

  1. 默认构造方法问题:
    1. 如果在类中没有定义构造方法,系统将自动提供一个无参构造方法;
    2. 如果在类中定义了构造方法,系统将不再提供默认的无参构造方法;
  2. 构造方法的重载问题:
    1. 只要我们给出了构造方法,不管是无参还是代参构造方法,系统都不在提供构造方法,如果我们想使用无参构造方法,就必须自己定义声明;
    2. 建议永远自己给出无参构造方法;

四、Student s=new Student()做了哪些事情

  1. Student.class文件加载到内存中方法区的class文件区域
  2. 栈内存中给s变量开辟一个内存空间;
  3. 堆内存中给new Student()对象申请一个空间;
  4. 成员变量进行默认初始化
  5. 成员变量进行显示初始化
  6. 成员变量进行构造方法初始化(即通过构造方法给成员变量进行初始化);
  7. 对象初始化完毕,把堆内存地址值赋值给s变量;
  • 总结:
    1. 构造方法对类进行初始化实际上是对类中的数据(即成员变量)进行初始化;
    2. 对成员变量进行初始化的时候,首先默认初始化,然后是显示初始化,最后才是构造方法初始化
      1. 默认初始化实际上就是给数据赋默认值(即0,false,null,'\u0000',0.0f,0.0d,...)的过程,
      2. 显示初始化实际上就是把手动赋值的值赋值给成员变量,比如private int num=1;这个1就是显示初始化,
      3. 构造方法初始化就是通过构造方法传递过来的值对成员变量进行初始化;
        - 因此才是这个初始化顺序,即默认初始化--->显示初始化--->构造方法初始化

五、 成员变量的定义规则##

  1. 变量什么时候定义成成员变量?
    • 如果这个变量是用来修饰这个类的信息的,那么该变量就应该定义为成员变量;
  2. 变量定义在哪里好呢?
    • 变量的范围越小越好,因为能及时被回收;

六、static 关键字

  1. 特点:
    1. 随着类的加载而加载;
      • 因此被static修饰的成员变量/成员方法也叫类成员
    2. 优先于对象而存在;(由第一点就可以推出第二点)
      • 自然也就优先于对象的成员变量和成员方法而存在;
    3. static修饰的成员变量成员方法被类的所有对象共享;
      • 即一个对象修改了某个成员变量的值,其他对象该成员变量的值也被修改了;
      • 这是我们判断是否使用static关键字的条件;
        • 即如果某个成员变量或成员方法是被所有对象共享的,那么这个成员变量或成员方法就应该被定义为静态的;
    4. 因为随着类的加载而加载,所以可以直接通过类名来调用,不用通过对象来调用(当然也可以通过对象名来调用);
  2. 注意事项:
    1. 在静态方法中没有this关键字;
    2. 静态方法只能访问静态的成员变量静态的成员方法
      • 即静态只能访问静态,非静态可以访问静态和非静态;
  3. static内存图解

static内存图解

注意到:

  1. static修饰的方法(如main方法)不用创建对象直接通过静态方法区加载到栈内存中;
  2. 当成员变量被static修饰的时候,该成员变量就存在于静态方法区静态区,而不是堆中,对象通过地址来指向该变量,这里就是静态标记,而类的成员方法不被static修饰时,存放在静态方法区的该对象专有的方法区,通过地址值指向这些方法,这就是方法标记
  3. 静态方法区中的静态区专门用来存放被static修饰的变量和方法,同时在方法区中专门有一个区域用来存放类的构造方法非静态成员方法

七、JVM内存简单区分

    • 特点:
      1. 每个线程都包含一个栈区,每个栈中的数据(基本类型,引用类型)都是私有的,其他栈不能访问,所以线程中的数据也是私有的;
      2. 栈中只存放基本数据类型以及引用数据类型的引用;
    • 特点:
      1. JVM只有一个堆区(heap),且被所有线程共享;
      2. 堆内存中存放的全是对象,且每个对象都包含一个与之对应的class的信息(class的目的是为了得到操作指令);
  1. 静态方法区

    • 特点:
      1. 被所有的线程共享;
      2. 静态方法区中包含的都是在整个程序中永远唯一的元素,如所有的class文件,static修饰的变量;
    • 静态方法区细分:
      1. class文件区域
        • 特点:
        1. java编译生成的所有class文件首先被加到该区域,然后该class文件中的成员信息再被加载到栈或者堆中;
      2. 静态区
        • 特点:
        1. 所有被`static`修饰的`变量`和`方法`存放区域,通过地址值进行引用;
        
      3. 方法区
        • 特点:
          1. 对象的构造方法非静态方法存放区域,通过地址值进行引用;
      4. 常量池
        • 特点:
          1. 存放一些常量;

八、代码块

  • 定义:在java中,用{}括起来的代码被称为代码块,根据位置和声明的不同,可以被分为以下几类:
    1. 局部代码块
      • 在方法中出现,用于限定变量生命周期,及早释放,提高内存利用率;
    2. 构造代码块
      • 定义:在构造方法的位置(即类中成员位置),用{}括起来的代码。每次调用构造方法前,都会先执行构造代码块,即先执行构造代码块,再执行构造方法;
      • 作用:
        1. 可以把多个构造方法中相同的代码放到一起,提高代码复用性;
        2. 对对象进行初始化
      • 说明:每调用一次构造方法,构造代码块就会被执行一次,因此构造代码块会被执行多次;
    3. 静态代码块
      • 定义:在类中成员位置,用{}括起来的代码,并且加了static修饰;
      • 作用:对类进行初始化
      • 说明:静态代码块只执行一次;
    4. 同步代码块
      • 定义: 在方法中出现,用{}括起来的代码,被synchroized修饰的代码块;
      • 作用:在多线程中保证{}中的代码是原子性操作,即保证线程安全;
  • 注意:
    1. 静态代码块,构造代码块,构造方法的执行顺序
      • 静态代码块 > 构造代码块 > 构造方法
    2. 静态代码块,构造代码块,构造方法的执行次数
      • 静态代码块:只执行一次;
      • 构造代码块:执行多次,构造方法被调用几次,构造代码块就执行几次;
      • 构造方法:执行多次;

九、继承

1. 继承注意事项

  1. 父类的私有成员(成员变量,成员方法)不能被继承;
  2. 子类不能继承父类的构造方法,但是可以通过super关键字访问父类的构造方法;
  3. 不要为了部分功能而去继承;
  4. 继承使用条件:是两个对象是集合中的属于关系时(两个类之间如果可以用is a来表述),就可以使用继承;

2.继承中类中成员之间的关系

1)继承中成员变量之间的关系

  1. 当子类和父类中的成员变量名称一样时,遵循就近原则,即
    1. 首先到子类的局部变量中找,有就使用,没有执行下一步;
    2. 在子类的成员变量中找,有就使用,没有执行下一步;
    3. 在父类的的成员变量中找,有就使用,没有就报错;

2)继承中构造方法之间的关系

  1. 子类中所有的构造方法默认都会访问父类的空参构造方法
    • 因为子类会继承父类中的数据,可能还会使用父类的数据。所以子类初始化之前,一定要先完成父类数据的初始化;
    • 子类每个构造方法的第一条语句默认是super();,不写也默认有;
  2. 如果父类没有无参构造方法,那么子类的构造方法就会报错,解决方法:
    1. 方法一:在父类中加一个无参构造方法;
    2. 方法二:通过super(...)去显式调用父类的带参构造方法;
    3. 方法三:子类通过this去调用本类的其他构造方法,从而间接调用父类的构造方法;
    • 总结:
      1. 子类中一定要有一个构造方法去访问父类的构造方法,否则父类数据就无法初始化;
      2. 实际上这里是分层初始化,即先初始化爷爷类,在初始化父亲类,最后才是初始化子类,而super()仅仅表示先初始化父类;
    • 注意事项:

    this(...)或者super(...)必须出现在第一条语句上;

    如果不是放在第一条语句上,就可能对父类的数据进行了多次初始化,所以必须放在第一条语句上;

4)继承中成员方法的关系

  1. 方法重写(和方法重载分开):
    • 子类中出现了和父类中一模一样的方法声明(即返回值类型,方法名,参数列表均相同);
    • 使用特点:
      1. 在访问重写方法时,如果子类有就先访问子类的,子类中没有,就到父类中找,如果没有找到,就报错;

      简单记:就近原则,先找自己的,有就用;如果没有再找父类的;

  2. 什么时候使用方法重写:
    • 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样既沿袭了父类的功能(通过super.成员方法()来调用父类的功能),又定义了子类特有的功能;
  3. 方法重写注意事项:
    1. 父类的私有方法不能被重写;

      因为父类私有方法就不能被继承,自然就不能被重写;
      2. 子类重写父类的方法时,访问权限不能更低,即子类方法的访问权限必须大于等于父类方法的访问权限;
      >子类重写父类方法的时候,最好声明一模一样。
      3. 父类是静态方法,子类也必须通过静态方法进行重写
      >父类是静态,子类重写时也必须是静态,否则会报错;
      >
      >父类是非静态,子类重写时也必须是非静态,否则会报错;

5)thissuper的区别与联系

  1. 表示的意义

    • this: 代表本类对象的引用;
    • super:代码父类对象的引用(实际上是父类存储空间的标识);
  2. 用法

    • 访问成员变量

    //表示访问本类的成员变量

    this.成员变量

    //表示访问本类的成员变量

    super.成员变量
    - 访问构造方法
    >//表示访问本类的构造方法
    >
    >this(...)
    >
    >//表示访问父类的构造方法,虽然不能继承父类的构造方法,但是可以通过super来访问
    >
    >//实际开发中,经常使用这种方法给父类的参数赋值,而且即使父类的参数是private修饰的,也不影响通过子类进行赋值;
    >
    >super(...)

    • 访问成员方法

    //表示访问本类的成员方法

    this.成员方法()

    //表示访问父类的成员方法

    super.成员方法()

成员变量属于每个对象 (而不是类)私有的值,它表征了该对象的某种状态,因此父类的成员变量子类可以访问,却不能修改,而成员方法表征了某一系列对象所具有的行为,因此子类可以覆盖父类的方法,实现自己的特有的功能,但是JVM是如何实现方法重写时,调用的是子类的方法的了?

十、final关键字

  1. final使用场景
    • 如果父类的某个成员变量只能被子类访问,但是不能被修改值;
    • 如果父类的某个成员方法只能被子类调用,但是不能被子类重写,
    • 如果某个就是最终类,不能有子类;
  2. final特点:final可以修饰成员变量成员方法
    1. final修饰,该类不能被继承;
    2. final修饰成员变量,这个成员变量只能被使用,不能被子类重新赋值;

      当一个变量被final修饰时,不能修改实际上就是常量,这是自定义常量,
      字面值常量就是字符串,true,10这种形式的;
      3. final修饰成员方法,这个成员方法只能被调用,不能被子类重写;

  3. final修饰局部变量
    1. 首先final可以修饰局部变量;

      区别于private等权限修饰符,这四个权限修饰符不能修饰局部变量,因为局部变量本来就只能在方法内部可以使用,方法外部不能使用,即本来就被封装起来了,权限修饰符再来修饰没有任何意义。
      2. final修饰基本数据类型时,基本类型的值不能发生改变;
      3. final修饰引用数据类型时,引用类型的地址值不能发生改变,但是该对象的堆内存的值是可以改变的,即该对象的成员变量的值是可以改变的,成员方法是可以被重写的;

  4. final修饰变量的初始化时机
    1. final修饰的变量只能赋值一次;
    2. 对于非静态常量而言,赋值在构造方法完毕之前完成;

    即case 1:如果被final修饰的变量已经赋值(例如private int num=10;),就不能在构造方法中给该常量赋值,否则会报错,

    case 2:如果被final修饰的变量未赋值(例如private int num;),可以在构造代码块中赋值,但不能再在构造方法中赋值,否则会报错;

    case 3:如果被final修饰的变量未赋值(例如private int num;),同时也没有在构造代码块中赋值,就可以在构造方法中赋值,此时不会报错;

十一、多态

1. 多态:同一个事物(对象),在不同时刻体现出来的不同状态

2. 多态的前提:

  1. 要有继承关系;
  2. 要有方法重写;
  3. 要有父类引用指向子类对象;

    Fu f=new Zi();

3. 多态中的成员访问特点

  1. 成员变量
>编译能否通过看左边,运行看左边;
>
>成员变量不存在重写概念,所以只能访问的父类的成员变量;
  1. 构造方法
>在创建子类对象(调用子类构造方法)的时候,会默认访问父类的构造方法(会先对父类进行初始化),对父类的数据进行初始化;
  1. 成员方法
>编译能否通过看左边,运行看右边;
>
>由于父类的成员方法可以子类重写,所以实际上调用的是子类的方法;
  1. 静态方法
>编译能否通过看左边,运行看左边
>
>静态和类相关,算不上重写,所以访问还是父类的;

总结:由于成员方法存在重写,所以他运行看子类的,成员变量静态方法均不存在重写概念,所以运行看父类的

4. 多态的优点与缺点

1)优点

  1. 提高了代码的维护性(通过继承实现);

  2. 提高了代码的扩展性(通过多态保证);

    由于子类可以重写父类的方法,所在在父类中调用父类的方法,在子类中重写这些方法,这样就保证了扩展性

     //父类
     class Animal {
        public void eat(){
           System.out.println("eat");
        }
    
        public void sleep(){
           System.out.println("sleep");
        }
     }
    
     class Dog extends Animal {
        public void eat(){
           System.out.println("狗吃肉");
        }
    
        public void sleep(){
           System.out.println("狗站着睡觉");
        }
     }
    
     class Cat extends Animal {
        public void eat() {
           System.out.println("猫吃鱼");
        }
    
        public void sleep() {
           System.out.println("猫趴着睡觉");
        }
     }
     
     class AnimalTool {
         private AnimalTool(){}
         
         //这里就体现出了由于多态而保证扩展性,如果这里传入Cat,Dog,就没有扩展性了,
         //每新添加一个Animal的子类,就需要把这个方法复制一份,扩展性就差了
         //扩展性就体现在这里传入的是Animal,而不是Animal的子类      
         public static void useAnimal(Animal a) {
             a.eat();
             a.sleep();
         }
     }
    

2)缺点

  1. 不能使用子类中的特有功能;
    • 解决:将父类的引用强制转换成对子类的引用(向下转型);

      对象间的转型问题

      向上转型

      Fu f=new Zi();

      向下转型

      Zi zi=(Zi)f;

3)多态继承中内存图解

多态继承中内存图解
  1. 这里应该可以理解super表示父类存储空间的标识这句话了吧;
  2. JVM是如何实现方法重写时,调用的是子类的方法的了?当没有重写时,调用的是父类的方法了?这里不明白;

4)多态中的对象变化内存图解

多态中的对象变化内存图解

注意:

  1. ClassCastException是在运行时才抛出的,因为在编译的时候,地址的指向没有出错,类是在运行时才加载到内存中去的,因此才会检测类的相关信息;

5)多态的种类

  1. 具体类多态(基本没用)

    即左边是一个具体类,右边是具体类的子类

  2. 抽象类多态(常用)

    即左边是一个抽象类,右边是抽象类的具体实现类

  3. 接口类多态 (用的最多)

    即左边是一个接口,右边是接口的具体实现类

十二、抽象类

(一)为什么会有抽象类

由于现实世界的复杂性,有些事物虽然是对象,但是是对一类事物的总结(如水果,动物),本身是一个抽象的对象,没有具体的属性值和具体的功能实现,如果这个时候我们给这种对象具体属性值和具体的功能,就违反了对现实事物的描述,所以是不对的,因此我只能对这种事物进行声明,表明他有某个功能,只要是这个事物的子类,就一定有这个功能,就要实现这个功能,这就是抽象类的来源;

(二)抽象类的特点

  1. 抽象类和抽象方法必须用abstract来修饰;
  2. 抽象类中不一定有抽象方法,但是有抽象方法的类一定是抽象类;
  3. 抽象类不能实例化,即不能用抽象类来创建对象;
    1. 因为抽象类不是具体的,不能造出具体的对象,否则就违反了现实上面的说明;
    2. 但是抽象类是有构造方法的,只是不能实例化对象而已;
    3. 抽象类的构造方法专门用来给子类访问,对父类进行数据初始化;
  4. 抽象类的子类
    1. 如果不想重写抽象方法,该子类可以是个抽象类;
    2. 如果子类是一个具体的类,就必须重写所有的抽象方法;
  5. 抽象类的实例化是通过具体的子类来实现的,是多态的方式,因此多态的所有特点,使用规则均适合抽象的实例化;

(三)抽象类的成员特点

  1. 成员变量
    • 既可以有变量,也可以有常量;(要和接口做区别)
  2. 构造方法
    • 有构造方法,用于子类访问父类数据的初始化;
  3. 成员方法
    • 抽象方法:强制要求子类做的事情
    • 非抽象方法:子类继承的事情,提高代码复用性;

(四)抽象类的注意事项

  1. 一个抽象类中可以没有抽象方法,这样做是为了不然抽象类创建对象(抽象类本来就不能创建对象),必须由其具体实现类创建对象
  2. abstract关键字不能和那些关键字一起出现
    1. abstract不能和private一起出现

      冲突,abstract修改的方法必须由具体子类实现,而private修饰的方法,子类无法访问;
      2. abstractfinal不能一起出现
      >冲突,abstract修饰的方法必须由具体子类实现,而final修饰的方法不能被子类重写;
      3. abstractstatic不能一起出现
      >无意义,abstract修饰的方法没有方法体,而static修饰的方法可以直接用类名调方法,这样相当于调的空方法;
      4. 抽象类中的非抽象方法可以用static修饰,这样用类名调方法就不会出现上述问题;

十三、接口

(一)接口的作用

提高事物功能的扩展性,弥补类的不足;

比如被训练的猴子可以表演杂技,但是表演杂技并不是动物和猴子固有的功能,因此不能写到动物类,猴子类,只能是被训练过的猴子拥有表演杂技的功能,这种表演杂技的功能就是额外的功能,既然不能写在类中,就通过接口实现这种功能;

(二)接口的特点

  1. 接口不能被实例化

    接口中的方法都是抽象的,不给出具体实现;(JDK 8中接口中的方法可以有方法实现)

    接口的实例化通过具体子类来实现,即通过多态的方式来实例化;

  2. 接口的子类
    1. 接口的子类可以是抽象类,但是没有意义
    2. 接口的子类可以是具体类,但是必须实现所有的抽象方法;

(三)接口的成员特点

  1. 成员变量
    • 接口中的变量默认public static final修饰,即接口中的成员变量只能是常量;
  2. 构造方法
    • 接口没有构造方法,因为接口主要是扩展功能的,而没有具体存在,必须依附于某个主体(实际上就是对象);

    所有的类都默认继承Object类,而Object类唯一构造方法就是Object(),因此如果在接口的实现类的构造方法中如果出现super()时,并不是访问的接口的接口的构造方法(接口本来就没有构造方法),而是访问的是Object类的构造方法;

  3. 成员方法
    • 只能是抽象方法,而且默认public abstract修饰;

注意:类与类之间只能是单继承关系,但是接口与接口之间可以是多继承的关系,即一个接口可以继承自多个接口;

(四)接口与抽象类的区别

  1. 成员不同
    • 抽象类
      1. 成员变量:可以是变量,也可以是常量;
      2. 构造方法:有
      3. 成员方法:可以有抽象方法,也可以有非抽象方法;
    • 接口
      1. 成员变量:只能是常量;
      2. 构造方法:无
      3. 成员方法:只能是抽象方法;
  2. 关系不同
    • 类与类:继承,单继承;
    • 类与接口:实现,可以多实现;
    • 接口与接口:单继承,多继承均可;
  3. 设计理念不同
    • 抽象类:被继承体现的是is a的关系,抽象类中定义的是该继承体系中的共性功能
    • 接口:被实现体现的是like a(像什么)的关系,接口中定义的是该继承体系的扩展功能(个性功能)

十四、内部类

(一)内部类的访问特点

  1. 内部类可以直接访问外部类的成员,包括private修饰的成员;
  2. 外部类要访问内部类的成员,必须创建内部类对象;

(二)成员内部类特点

  1. 如何直接访问内部类的成员变量,成员方法;

    外部类名.内部类名 对象名=外部类对象.内部类对象;

    Outer.Innner oi=new Outer().new Inner();

  2. 内部类用private修饰(为了保证数据安全),这时内部类就不能被访问,通常做法是,在外部类中定义一个方法,在方法内部访问内部类(当然有逻辑控制),这样就可以通过外部类来访问内部类;
  3. 当内部类被static修饰时(这样做是为了方便数据访问),内部类就不能访问外部类非静态成员,因为静态只能访问静态;

    成员内部类被static修饰时,访问方式为

    外部类名.内部类名 对象名=new 外部类名.内部类名();
    Outer.Inner oi=new Outer.Inner();

  4. 内部类的方法访问外部类的成员:通过外部类名限定this对象;Outer.this.method()Outer.this.params

(三)局部内部类

  1. 可以直接访问外部类的成员;
  2. 在局部位置(即方法内部),可以创建内部类对象,通过对象名调用内部类方法,来使用局部内部类的功能;
  3. 局部内部类访问局部变量时,局部变量必须被final修饰;
    • 因为局部变量随着方法的调用而调用,随着方法的调用结束而消失,而堆内存的内容并不会立即消失,所以通过final修饰变成常量,即使局部变量消失了,常量的值还存在,依然可以使用。实际上,通过反编译class文件可以发现,被final修饰的局部变量,在内部类中直接是常量,而不是局部变量的变量名。

(四)匿名内部类

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345