CS2312 Lecture 4

References vs Values

 
•Primitive types are basic java types
–int, long, double, boolean, char, short, byte, float
–The actual values are stored in the variable
 
•Reference types are arrays and objects
–String, int[], LinkedList, …
 
variable - fixed size
Primitives are small enough to fit into a variable;
But Object too big to fit in a variable, so it stores an object's location
 
References:
The object’s location is called a reference  
== compares the references 
Using = updates the reference
 
 
An object variable is really a reference to the object.  – A pointer
Need to 'dereference' the variable to access method and fields 
E.g,.
person.getName();
person.number;
Can have 2 variables reference the same object
E.g,. 
Integer a = new Integer(5);
Integer b = a;  
// a and b reference the same object

 

Remember that primitive arguments are passed by value.      

If you change a primitive argument inside of a method, the variable in the calling method will remain unchanged. 

public class test {

    public static int meth(int a, int b) {


        a = a * 2; b = b * 3;
        return a + b;
    }

    public static void main(String args[]){
        int x = 5;
        int y= 10;
        int z = 0;

        z = meth(x,y);
        System.out.println(x);   // 5
        System.out.println(y);   // 10
        System.out.println(z);   // 40

    }
}

Object Arguments are pass by reference  

In Java an object argument references the object just as a normal variable  

Any changes to the object in the method are visible in the calling method 

 


Java is Pass-by-Value

Java is always pass-by-value. Unfortunately, they decided to call the location of an object a "reference". When we pass the value of an object, we are passing the reference to it. This is confusing to beginners.

It goes like this:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    // we pass the object to foo
    foo(aDog);
    // aDog variable is still pointing to the "Max" dog when foo(...) returns
    aDog.getName().equals("Max"); // true, java passes by value
    aDog.getName().equals("Fifi"); // false 
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // change d inside of foo() to point to a new Dog instance "Fifi"
    d = new Dog("Fifi");
    d.getName().equals("Fifi"); // true
}

In the example above aDog.getName() will still return "Max". The value aDog within main is not changed in the function foo with the Dog "Fifi" as the object reference is passed by value. If it were passed by reference, then the aDog.getName() in main would return "Fifi" after the call to foo.

Likewise:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // this changes the name of d to be "Fifi"
    d.setName("Fifi");
}

In the above example, Fifi is the dog's name after call to foo(aDog) because the object's name was set inside of foo(...). Any operations that foo performs on d are such that, for all practical purposes, they are performed on aDog itself (except when d is changed to point to a different Doginstance like d = new Dog("Boxer")).

 
This code in Java won't work
public void badSwap(int var1, int var2)  
{  
  int temp = var1;  
  var1 = var2;  
  var2 = temp;  
} 

 


java在类的内部创建本类的对象

public class Direction{
    public static final Direction LEFT = new Direction();
    public static final Direction RIGHT = new Direction();

    //constructor
    public Direction(){...}
}

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的引用访问涉及到3个内存区域:JVM栈,堆,方法区。

  以最简单的本地变量引用:Object obj = new Object()为例:

  • Object obj表示一个本地引用,存储在JVM栈的本地变量表中,表示一个reference类型数据;

  • new Object()作为实例对象数据存储在堆中;

  • 堆中还记录了Object类的类型信息(接口、方法、field、对象类型等)的地址,这些地址所执行的数据存储在方法区中;

具体的实现方式有很多种,句柄是其中一种,关系如图所示。

类本身的信息,类实例数据,以及指向对象的引用信息分别放在 java 的方法区和栈区以及堆区。

 

java加载顺序是这样的:

  1. jvm先加载了方法区的类定义(但此时并没有实例化这个类)

  2. public static final Direction LEFT = new Direction(); 是个静态变量,所以这个变量也会在 jvm 第一次读取方法区定义时被装载进方法区中。

  3. 同时,这也意味着,在装载这个变量的同时,也在堆区实例化了这个类的实例。

注意这里面的关键点,因为 FRONT 变量是静态变量,而加载类定义只会加载一次,所以这个静态变量也只可能加载一次。并不会像非静态变量一样因为循环引用重复实例化而导致栈溢出

posted @ 2018-02-09 14:32  Charonnnnn  阅读(212)  评论(0编辑  收藏  举报