• Java引用和内部类


    引用

    引用变量

    引用相当于一个 “别名”, 也可以理解成一个指针.

    创建一个引用只是相当于创建了一个很小的变量, 这个变量保存了一个整数, 这个整数表示内存中的一个地址.

    new 出来的数组肯定是在堆上开辟的空间,那么在栈中存放的就是引用,引用存放的的就是一个对象的地址,代表指向关系.

    int[]array2=array1;
    
    • 1

    就是在栈中再开辟一个空间作为引用,这两个引用存放的都是那个数组的地址.

    总结: 所谓的 “引用” 本质上只是存了一个地址. Java 将数组设定成引用类型, 这样的话后续进行数组参数传参, 其实只是将数组的地址传入到函数形参中. 这样可以避免对整个数组的拷贝(数组可能比较长, 那么拷贝开销就会很大).

    image-20231011215540178

    java中引用必须初始化

    如果确实不知道是哪块空间的引用,可以初始化为null

    JVM内存空间分布

    image-20231011220459714

    • 虚拟机栈(JVM Stack): 重点是存储局部变量表(当然也有其他信息). 我们刚才创建的 int[] arr 这样的存储地址的引用就是在这里保存.
    • 本地方法栈(Native Method Stack): 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是Native方法的局部变量. 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的.
    • 堆(Heap): JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2,3} )
    • 方法区(Method Area): 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据. 方法编译出的的字节码就是保存在这个区域.
    • 运行时常量池(Runtime Constant Pool): 是方法区的一部分, 存放字面量(字符串常量)与符号引用. (注意 从 JDK1.7 开始, 运行时常量池在堆上)
    • 程序计数器 (PC Register): 只是一个很小的空间, 保存下一条执行的指令的地址.

    image-20231011220731641

    public class myArray {
        public static void main(String[] args) {
            int[] arr1={1,2,3,4,5};
            int []arr2=new int[]{5,4,3,2,1};
    
            int[]p1=arr1;
            int[]p2=arr2;
    
            p1[0]=100;
            p2[0]=100;
    
            System.out.println(arr1[0]);//100
            System.out.println(arr2[0]);//100
        }
        public static int[] tranform(int[]arr){//传的是引用,会将堆中数据改变
            int[] ret=new int[arr.length];//要想不被改变,再开一个空间计算返回
            for(int i=0;i<arr.length;i++){
                ret[i]=arr[i]*2;
            }
            return ret;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    多个引用指向同一个对象

    image-20231011221525087

    引用传参

    image-20231011223509392

    import java.util.Arrays;
    public class myArray {
        public static void func1(int[]arr){//生成arr形参,引用中和arr1中指向同一个对象
            arr=new int[]{15,16,17,18};//arr更改指向对象
        }
        public static void func2(int[]arr){
            arr[0]=100;
        }
        public static void main(String[] args) {
            int[]arr1=new int[]{1,2,3,4};
    //        func1(arr1);
            func2(arr1);
            System.out.println(Arrays.toString(arr1));//func1之后: 1 2 3 4
            //func2后: 100 2 3 4
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    image-20231011222912130

    image-20231011222929443

    内部类

    静态内部类

    • 无法直接访问外部类的非静态方法.
      • 可以创建外部类的对象,使用引用访问.
    • 外部类可以访问静态内部类中的所有的成员,即使在静态内部类中是private修饰的.
    package demo1;
    
    class OuterClass{
        public int data1=10;
        private int data2=20;
        public static int data3=30;
    
        public void test1(){
            System.out.println(data1);
            System.out.println(data2);
            System.out.println(data3);
    
            InnerClass innerClass =new InnerClass();
            System.out.println(innerClass.data4);
            System.out.println(innerClass.data5);
            System.out.println(InnerClass.data6);//静态的用内部类类名访问,尽量避免使用对象
        }
        static class InnerClass{//静态内部类
    
            public int data4=40;
            private int data5=50;
            public static int data6=60;
            public void func(){
                //所有静态的,都是不依赖于对象的,所以无法访问OuterClass需要用引用访问的成员
    //            System.out.println(data1);
    //            System.out.println(data2);//error
                OuterClass outerClass =new OuterClass();
                System.out.println(outerClass.data1);
                System.out.println(outerClass.data2);
                System.out.println(data3);
                System.out.println(data4);
                System.out.println(data5);
                System.out.println(data6);
            }
        }
    }
    public class test1 {
        public static void main(String[] args) {
            OuterClass.InnerClass innerClass = new OuterClass.InnerClass();
            innerClass.func();
            //10
            //20
            //30
            //40
            //50
            //60
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    实例内部类

    • 在实例内部类中不能定义静态成员.彼此产生矛盾.

    • 实例内部类的实例化对象需要 外部类对象 调用才能实现.

    • 当内部类中存在和外部类成员变量名字冲突时,this肯定是内部类的.如果想要访问外部类的,需要使用

      OuterClass.this.data1

    • 当外部类加载时,这个非静态内部类是不会加载的

    class OuterClass2{
        public int data1=10;
        private int data2=20;
        public static int data3=30;
    
        public void test1(){
            System.out.println(data1);
            System.out.println(data2);
            System.out.println(data3);
    
            InnerClass2 innerClass2 =new InnerClass2();
            System.out.println(innerClass2.data4);
            System.out.println(innerClass2.data5);
            System.out.println(InnerClass2.data6);//使用类名访问
        }
        class InnerClass2{//非静态内部类(实例内部类)
    
            public int data1 = 1111;
            public int data4=40;
            private int data5=50;
    //        public static int data6=60;//内部类是依赖于外部类对象的,静态的是不依赖于对象的,产生矛盾
            public static final int data6=60;//此时data6是常量,是在程序编译时就已经确定的
            public void func(){
                System.out.println(data1);//1111 this是内部类的this
                System.out.println(OuterClass2.this.data1);
                System.out.println(data2);
                System.out.println(data3);
                System.out.println(data4);
                System.out.println(data5);
                System.out.println(data6);
            }
        }
    }
    public class test1{
        public static void main(String[] args) {
            OuterClass2 outerClass2 = new OuterClass2();//此时的实力内部类可以理解为外部类的一个成员,需要对象调用
            OuterClass2.InnerClass2 innerClass2 =outerClass2. new InnerClass2();
            innerClass2.func();
            outerClass2.test1();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    匿名内部类

    interface IA{
        void func();
    }
    class AA implements IA{
        @Override
        public void func() {
            System.out.println("AA func!");
        }
    }
    public class test1{
        public static void main(String[] args) {
            new IA(){//有一个类,实现了IA接口,重写了func函数,但是这个类没有名字.不会生成字节码文件
                @Override
                public void func() {
                    System.out.println("hello 匿名内部类!");
                }
            }.func();//hello 匿名内部类!
        }
        public static void main1(String[] args) {//正常实现接口
            IA ia = new AA();
            ia.func();//AA func!
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
  • 相关阅读:
    MIT课程分布式系统学习01——Introduction
    C语言数据类型转换
    ABAP Json和对象的转换
    去掉macOS终端命令行前的(base)
    ChatGPT 提示词技巧一本速通
    【分布式】: 幂等性和实现方式
    幸福心理与抗逆力培养的工控系统安全实验课程研究
    12 款小众宝藏AI工具,90% 的开发者不了解
    迅为RK3568开发板文件互传与OTA升级
    【重温经典C语言】~c语言中%x、%c、%d、%x等等等、c语言取地址符&的作用、C语言中的 联合体
  • 原文地址:https://blog.csdn.net/weixin_58843717/article/details/134551722