84669 Lernen von Personen
152542 Lernen von Personen
20005 Lernen von Personen
5487 Lernen von Personen
7821 Lernen von Personen
359900 Lernen von Personen
3350 Lernen von Personen
180660 Lernen von Personen
48569 Lernen von Personen
18603 Lernen von Personen
40936 Lernen von Personen
1549 Lernen von Personen
1183 Lernen von Personen
32909 Lernen von Personen
ringa_lee
先明确几个概念,java代码是跑在jvm中的,而jvm的内存区域划分为这么几个模块:
程序计数器(Program Counter Register):程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行号指示器。字节码解释器在工作时,会通过改变这个计数器的值来取下一条语句指令。
虚拟机栈(JVM Stack):一个线程的每个方法在执行的同时,都会创建一个栈帧(Statck Frame),栈帧中存储的有局部变量表、操作站、动态链接、方法出口等,当方法被调用时,栈帧在JVM栈中入栈,当方法执行完成时,栈帧出栈。
本地方法栈(Native Method Statck):本地方法栈在作用,运行机制,异常类型等方面都与虚拟机栈相同,唯一的区别是:虚拟机栈是执行Java方法的,而本地方法栈是用来执行native方法的,在很多虚拟机中(如Sun的JDK默认的HotSpot虚拟机),会将本地方法栈与虚拟机栈放在一起使用。
堆区(Heap):堆区是理解Java GC机制最重要的区域,没有之一。在JVM所管理的内存中,堆区是最大的一块,堆区也是Java GC机制所管理的主要内存区域,堆区由所有线程共享,在虚拟机启动时创建。堆区的存在是为了存储对象实例,原则上讲,所有的对象都在堆区上分配内存(不过现代技术里,也不是这么绝对的,也有栈上直接分配的)。
方法区(Method Area):(也被称为永久代),方法区是各个线程共享的区域,用于存储已经被虚拟机加载的类信息(即加载类时需要加载的信息,包括版本、field、方法、接口等信息)、final常量、静态变量、编译器即时编译的代码等。
直接内存(Direct Memory):直接内存并不是JVM管理的内存,可以这样理解,直接内存,就是JVM以外的机器内存,比如,你有4G的内存,JVM占用了1G,则其余的3G就是直接内存,JDK中有一种基于通道(Channel)和缓冲区(Buffer)的内存分配方式,将由C语言实现的native函数库分配在直接内存中,用存储在JVM堆中的DirectByteBuffer来引用。由于直接内存收到本机器内存的限制,所以也可能出现OutOfMemoryError的异常。
明白这几个基本概念以后再来看看题主疑惑的地方。其实题主疑惑的是在java中,对象的引用是如何实现的。为什么可以在定义一个类的同时,定义自己的引用,同时如果再实例化了这个引用以后,难道不会导致无线循环引用下去吗?
别急我们先来分析下java中一个引用是怎么实现的:
一个Java的引用访问涉及到3个内存区域:JVM栈,堆,方法区。
以最简单的本地变量引用:Object obj = new Object()为例:
Object obj表示一个本地引用,存储在JVM栈的本地变量表中,表示一个reference类型数据;
new Object()作为实例对象数据存储在堆中;
堆中还记录了Object类的类型信息(接口、方法、field、对象类型等)的地址,这些地址所执行的数据存储在方法区中;
具体的实现方式有很多种,句柄是其中一种,关系如图所示。
看到这里应该就明白了。类本身的信息,类实例数据,以及指向对象的引用信息分别放在 java 的方法区和栈区以及堆区。
在题主的例子中,java加载顺序是这样的:
jvm先加载了方法区的类定义(但此时并没有实例化这个类)
因为 public static final Direction FRONT = new Direction(); 是个静态变量,所以这个变量也会在 jvm 第一次读取方法区定义时被装载进方法区中。
public static final Direction FRONT = new Direction();
同时,这也意味着,在装载这个变量的同时,也在堆区实例化了这个类的实例。
注意这里面的关键点,因为 FRONT 变量是静态变量,而加载类定义只会加载一次,所以这个静态变量也只可能加载一次。并不会像非静态变量一样因为循环引用重复实例化而导致栈溢出。
推荐你看看R大的回答
先有Class还是先有Object?https://www.zhihu.com/questio...
说说你的理解,为什么类里面不能创建自己的对象?这几个变量加上了static后就变成了类的属性了,只会创建一次。
如果自己都不能创建自己,那其他类就更不能了。这样的话这个类怎么实例化……
设计模式:单例模式
本质是对java的面向对象编程的不理解。看看23种设计模式你可能就会理解
构造函数也是一个方法。
具有 private 访问权限的方法表示私有的,只有本类可见。
private
所以,本类可以调用具有 private 访问权限的构造函数实例化一个对象。
使用内部类的原因:每个内部类都能独立的继承自一个(接口的)实现,所以无论外部类是否已经继承了某个(接口的)的实现,对内部类都没有影响。实际上内部类有效的实现了“多重继承”,就是说,内部类允许继承多个非接口类型。
我们知道内部类自动拥有对外部类所有成员的访问权,那么这是如何做到的吗?当某个外部类对象创建了一个内部类对象时,此内部类对象必定会秘密的捕获一个指向那个外部类对象的引用。然后,在你访问外部类的成员时,就是用那个引用来选择外部类的成员。当然这些细节是编译器处理,并且这里的内部类是非static的。如果一个类都不能创建自己的类对象,那我要你这个类何用?啊,哈哈哈哈,开玩笑咯
先明确几个概念,java代码是跑在jvm中的,而jvm的内存区域划分为这么几个模块:
程序计数器(Program Counter Register):程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行号指示器。字节码解释器在工作时,会通过改变这个计数器的值来取下一条语句指令。
虚拟机栈(JVM Stack):一个线程的每个方法在执行的同时,都会创建一个栈帧(Statck Frame),栈帧中存储的有局部变量表、操作站、动态链接、方法出口等,当方法被调用时,栈帧在JVM栈中入栈,当方法执行完成时,栈帧出栈。
本地方法栈(Native Method Statck):本地方法栈在作用,运行机制,异常类型等方面都与虚拟机栈相同,唯一的区别是:虚拟机栈是执行Java方法的,而本地方法栈是用来执行native方法的,在很多虚拟机中(如Sun的JDK默认的HotSpot虚拟机),会将本地方法栈与虚拟机栈放在一起使用。
堆区(Heap):堆区是理解Java GC机制最重要的区域,没有之一。在JVM所管理的内存中,堆区是最大的一块,堆区也是Java GC机制所管理的主要内存区域,堆区由所有线程共享,在虚拟机启动时创建。堆区的存在是为了存储对象实例,原则上讲,所有的对象都在堆区上分配内存(不过现代技术里,也不是这么绝对的,也有栈上直接分配的)。
方法区(Method Area):(也被称为永久代),方法区是各个线程共享的区域,用于存储已经被虚拟机加载的类信息(即加载类时需要加载的信息,包括版本、field、方法、接口等信息)、final常量、静态变量、编译器即时编译的代码等。
直接内存(Direct Memory):直接内存并不是JVM管理的内存,可以这样理解,直接内存,就是JVM以外的机器内存,比如,你有4G的内存,JVM占用了1G,则其余的3G就是直接内存,JDK中有一种基于通道(Channel)和缓冲区(Buffer)的内存分配方式,将由C语言实现的native函数库分配在直接内存中,用存储在JVM堆中的DirectByteBuffer来引用。由于直接内存收到本机器内存的限制,所以也可能出现OutOfMemoryError的异常。
明白这几个基本概念以后再来看看题主疑惑的地方。其实题主疑惑的是在java中,对象的引用是如何实现的。为什么可以在定义一个类的同时,定义自己的引用,同时如果再实例化了这个引用以后,难道不会导致无线循环引用下去吗?
别急我们先来分析下java中一个引用是怎么实现的:
一个Java的引用访问涉及到3个内存区域:JVM栈,堆,方法区。
以最简单的本地变量引用:Object obj = new Object()为例:
Object obj表示一个本地引用,存储在JVM栈的本地变量表中,表示一个reference类型数据;
new Object()作为实例对象数据存储在堆中;
堆中还记录了Object类的类型信息(接口、方法、field、对象类型等)的地址,这些地址所执行的数据存储在方法区中;
具体的实现方式有很多种,句柄是其中一种,关系如图所示。
看到这里应该就明白了。类本身的信息,类实例数据,以及指向对象的引用信息分别放在 java 的方法区和栈区以及堆区。
在题主的例子中,java加载顺序是这样的:
jvm先加载了方法区的类定义(但此时并没有实例化这个类)
因为
public static final Direction FRONT = new Direction();
是个静态变量,所以这个变量也会在 jvm 第一次读取方法区定义时被装载进方法区中。同时,这也意味着,在装载这个变量的同时,也在堆区实例化了这个类的实例。
注意这里面的关键点,因为 FRONT 变量是静态变量,而加载类定义只会加载一次,所以这个静态变量也只可能加载一次。并不会像非静态变量一样因为循环引用重复实例化而导致栈溢出。
推荐你看看R大的回答
说说你的理解,为什么类里面不能创建自己的对象?
这几个变量加上了static后就变成了类的属性了,只会创建一次。
如果自己都不能创建自己,那其他类就更不能了。这样的话这个类怎么实例化……
设计模式:单例模式
本质是对java的面向对象编程的不理解。看看23种设计模式你可能就会理解
构造函数也是一个方法。
具有
private
访问权限的方法表示私有的,只有本类可见。所以,本类可以调用具有
private
访问权限的构造函数实例化一个对象。使用内部类的原因:每个内部类都能独立的继承自一个(接口的)实现,所以无论外部类是否已经继承了某个(接口的)的实现,对内部类都没有影响。实际上内部类有效的实现了“多重继承”,就是说,内部类允许继承多个非接口类型。
我们知道内部类自动拥有对外部类所有成员的访问权,那么这是如何做到的吗?当某个外部类对象创建了一个内部类对象时,此内部类对象必定会秘密的捕获一个指向那个外部类对象的引用。然后,在你访问外部类的成员时,就是用那个引用来选择外部类的成员。当然这些细节是编译器处理,并且这里的内部类是非static的。
如果一个类都不能创建自己的类对象,那我要你这个类何用?啊,哈哈哈哈,开玩笑咯