對(duì)象與引用對(duì)象
-
對(duì)象引用
對(duì)于一個(gè)簡單的例子:
Person p=new Person("Jim",18);
內(nèi)存表示:
1棧內(nèi)存中p并不是一個(gè)對(duì)象本身,而只是一個(gè)指向?qū)ο蟮闹羔?引用).用等號(hào)賦值時(shí),是將右側(cè)堆內(nèi)存中對(duì)象的地址賦予給對(duì)象引用.
-
引用賦值
若繼續(xù)進(jìn)行操作的話:
Person p2=p; p2.name="Lucy";那么p的名字也受影響變成了lucy.
2當(dāng)我們將一個(gè)引用賦值給另一個(gè)引用時(shí),我們實(shí)際上復(fù)制的是對(duì)象的地址。兩個(gè)引用將指向同一對(duì)象.其中一個(gè)引用修改對(duì)象,另一個(gè)引用也看得見.
-
垃圾回收
一個(gè)對(duì)象可以有多個(gè)引用,一個(gè)引用不可以有多個(gè)對(duì)象.同時(shí),一個(gè)對(duì)象要是沒有引用,會(huì)被垃圾回收.
3
-
參數(shù)傳遞
java只有值傳遞,java只有值傳遞,java只有值傳遞!
如果傳遞參數(shù)類型是基本數(shù)據(jù)類型,那么傳過來的就是這個(gè)參數(shù)的一個(gè)副本,也就是這個(gè)原始參數(shù)的值,這個(gè)與c的傳值是一樣的。如果在函數(shù)中改變了副本的 值不會(huì)改變?cè)嫉闹?
如果傳遞參數(shù)類型是引用類型,那么傳過來的就是這個(gè)引用參數(shù)的副本,這個(gè)副本存放的是參數(shù)的地址。如果在函數(shù)中沒有改變這個(gè)副本的地址,而是改變了地址中的 值,那么在函數(shù)內(nèi)的改變會(huì)影響到傳入的參數(shù)。如果只是改變地址,指針指來指去,而不去改變值,是不會(huì)影響參數(shù)的值。
引用一段Clara_babybear代碼作為例子:public class ParamTest { public static void main(String[] args){ /** * Test 1: Methods can't modify numeric parameters */ System.out.println("Testing tripleValue:"); double percent = 10; System.out.println("Before: percent=" + percent); tripleValue(percent); System.out.println("After: percent=" + percent); /** * Test 2: Methods can change the state of object parameters */ System.out.println("\nTesting tripleSalary:"); Employee harry = new Employee("Harry", 50000); System.out.println("Before: salary=" + harry.getSalary()); tripleSalary(harry); System.out.println("After: salary=" + harry.getSalary()); /** * Test 3: Methods can't attach new objects to object parameters */ System.out.println("\nTesting swap:"); Employee a = new Employee("Alice", 70000); Employee b = new Employee("Bob", 60000); System.out.println("Before: a=" + a.getName()); System.out.println("Before: b=" + b.getName()); swap(a, b); System.out.println("After: a=" + a.getName()); System.out.println("After: b=" + b.getName()); } private static void swap(Employee x, Employee y) { Employee temp = x; x=y; y=temp; System.out.println("End of method: x=" + x.getName()); System.out.println("End of method: y=" + y.getName()); } private static void tripleSalary(Employee x) { x.raiseSalary(200); System.out.println("End of method: salary=" + x.getSalary()); } private static void tripleValue(double x) { x=3*x; System.out.println("End of Method X= "+x); }
}
輸出結(jié)果
```java
Testing tripleValue:
Before: percent=10.0
End of Method X= 30.0
After: percent=10.0
Testing tripleSalary:
Before: salary=50000.0
End of method: salary=150000.0
After: salary=150000.0
Testing swap:
Before: a=Alice
Before: b=Bob
End of method: x=Bob //可見引用的副本進(jìn)行了交換
End of method: y=Alice
After: a=Alice //引用本身沒有交換
After: b=Bob
對(duì)象內(nèi)存表示
-
對(duì)象內(nèi)存基礎(chǔ)
java中內(nèi)存主要包含4塊,即heap(堆內(nèi)存)、stack(棧內(nèi)存)、data segment(靜態(tài)變量或是常量存放區(qū))、codesegment(方法區(qū)).
堆內(nèi)存: 存放的是new出的對(duì)象,new出的對(duì)象只包含成員變量.
棧內(nèi)存: 存放的是局部成員變量。對(duì)于基本的數(shù)據(jù)類型存放的是基本變量的值,而對(duì)于對(duì)象變量,存放的是堆內(nèi)存的地址。
靜態(tài),常量區(qū): 存放的是靜態(tài)變量(類變量)或是常量(字符串常量和基本類型常量(public static final))。
方法區(qū): 存放的是對(duì)象的方法。因此即使new出多個(gè)對(duì)象也是只存在一個(gè)方法。
對(duì)于一段簡單的代碼:
class Student{
private String name;
private int id;
private Class class;
public void study(){};
}
class Class{
private String name;
private int id;
public void exam(){};
}
public class MainClass(){
public static void main(String[] args){
Student s=new Student();
s.name="Jim";
s.id=001;
s.study();
Student s2=new Student();
s2.name="Lucy"
Class c=new Class();
c.name="三班";
c.id=03;
s.class=c;
c.exam();
}
}
關(guān)于該段內(nèi)存表示:

考慮一些復(fù)雜的情況:
class Student{
private String name;
private int id;
private Class class;
public Student(String iname,int iid){
name=iname;
id=iid;
}
public void study(int i){
id=i;
class.exam();
};
}
class Class{
private String name;
private int id;
public void exam(){};
}
public class MainClass(){
public static void main(String[] args){
Student s=new Student("Jim",001);
Class c=new Class();
c.name="三班";
c.id=03;
s.class=c;
s.study(010);
}
}
內(nèi)存表示:

形參,局部變量基本類型的值都是在棧內(nèi)存中
-
關(guān)于繼承與多態(tài)內(nèi)存表示
一個(gè)簡單的例子:
class Animal(){
private String name;
public void eat(){}
public void run(){
eat();
}
}
class Birds extends Animal(){
private Wing wing;
public void eat(){}
}
public MainClass(){
public static void main(String[] args){
Animal a=new Birds();
a.eat();
a.run();
}
}

分析方法調(diào)用過程:
創(chuàng)建的對(duì)象a指向Birds類,this層為Birds類那層.
a.eat()方法從this層調(diào)起,調(diào)用Bird類的eat()方法.
a.run()方法從this層調(diào)起,沒有則去調(diào)Animal的run()方法.這時(shí)候run()方法要調(diào)用eat()方法,同樣從this層調(diào)起,調(diào)用Birds類的eat()方法.
-
串的內(nèi)存表示
String是一個(gè)特殊的包裝類數(shù)據(jù)。可以用
String str = new String("abc");和String str = "abc";兩種的形式來創(chuàng)建.
第一種用new創(chuàng)建的對(duì)象會(huì)放在堆中,每調(diào)用一次會(huì)創(chuàng)建一個(gè)新的對(duì)象.
第二種先在棧中創(chuàng)建一個(gè)對(duì) String類的對(duì)象引用變量str,然后通過符號(hào)引用去字符串常量池 里找有沒有"abc",如果沒有,則將"abc"存放進(jìn)字符串常量池 ,并令str指向”abc”,如果已經(jīng)有”abc” 則直接令str指向“abc”。
值得一提的是:- new String()和new String("")都是申明一個(gè)新的空字符串,是空串不是null;
-
"fredal"=="fre"+"dal";
"fredal"!="fre"+new Stirng("dal");
String s="a";String s2="b";"ab"!=s+s2 - equals對(duì)于String簡單來說就是比較兩字符串的Unicode序列是否相當(dāng);而==是比較兩字符串的地址是否相同,也就是是否是同一個(gè)字符串的引用。
- String是不可變的,所有會(huì)產(chǎn)生很多臨時(shí)變量,可以采用StringBuffer,StringBuilder.
- 關(guān)于final,例如
final String ia="a",由于對(duì)于final修飾的變量,它在編譯時(shí)被解析為常量值的一個(gè)本地拷貝存儲(chǔ)到自己的常量池中或嵌入到它的字節(jié)碼流中,用的時(shí)候ia和"a"沒啥區(qū)別.2的第三個(gè)加了final就是相等的.
-
數(shù)組內(nèi)存表示
首先數(shù)組的初始化有靜態(tài)初始化和動(dòng)態(tài)初始化,前者類似于
String[] names = { "Jim", "Lili", "Lucy" }這種,后者則是String[] names = new String[4];這種.
根據(jù)數(shù)組的存儲(chǔ)對(duì)象的類型呢也有兩種,一種存基本數(shù)據(jù)類型,一種引用數(shù)據(jù)類型.
當(dāng)然還有一維數(shù)組和多維數(shù)組的區(qū)別.
一股腦舉個(gè)例子:String[] names={"Jim", "Lili", "Lucy"}; int [] number=new int[3]; for(int i=1;i<=3;i++){number[i-1]=i} Animal[] animal = new Animal[2]; Animal cat = new Animal("cat", 1); Animal dog = new Animal("dog", 2); animal[0] = dog; animal[1] = cat; int[][] number2=new int[2][]; number2[0]=new int[2]; number2[1]=new int[3];7
更多文章與相關(guān)下載請(qǐng)看擴(kuò)展閱讀



