JAVA與指針
首先,提個問題:JAVA中沒有指針,JAVA中有指針,哪個一個對呢?
答:都對,JAVA中沒有指針,因爲我們不能對指針直接操作,像C那樣用->來訪問變量,同時不能修改指針值
JAVA有指針,因爲JDK中封裝了指針。(現在我們就來找到這個指針)
注意:
1、指針變量有兩層含義
1) 指針變量裏存的是地址(它指向的變量的首地址)。
2) 指針變量有類型,類型說明了該指針指向的變量在內存中的範圍(大小)。
2、使用創建一個對象包括聲明和定義。
1) 聲明是指定義一個指向該對象的指針變量。
2) 定義是指用new關鍵字在堆內存中創建了對象,並把他的首地址付給那個指針變量。
這樣,很多概念就很容易理解了。
一、值傳遞和引用傳遞
舉例說明:
測試程序爲:
public class Student {
String name;
}
public class TestClass {
public static void main(String[] args) {
Student stu1;
stu1 = new Student();
stu1.name = "小明";
int a = 10;
System.out.println("改變前,stu1名字爲" + stu1.name);
System.out.println("改變前,a的值爲" + a);
TestClass myTest = new TestClass();
myTest.change(a, stu1);
System.out.println("改變後,stu1名字爲" + stu1.name);
System.out.println("改變後,a的值爲" + a);
}
void change(int num, Student student) {
num= num + 10;
student.name = "小強";
}
}
運行結果爲:
改變前,stu1名字爲小明
改變前,a的值爲10
改變後,stu1名字爲小強
改變後,a的值爲10
在內存中的表示如下:
轉變前:
轉變時:把stu1的值(36DF)傳給student,使student指向36DF。小明變成了小強。
把a的值10傳給了num,使num變成了20。
轉變後:student和num兩個變量被銷燬。
所以,stu1指向的內容改變了,a的值不變。這也就是值傳遞與引用傳遞的區別。從中也可以看出就把Student stu1;理解成聲明瞭一個指向Student類的指針變量就可以了。
這裏只用String這個類特殊。
public class TestClass {
public static void main(String[] args) {
String name = "abc";
System.out.println(name);
TestClass myTest = new TestClass();
myTest.change(name);
System.out.println(name);
}
void change(String str) {
str = "abcd";
}
}
運行結果爲:
改變前name爲abc
改變前name爲abc
按照前面的分析改變後的結果應該爲:abcd,如圖:
但是你會發現問題,上文強調過指針的第二層含義爲定義了變量在堆內存中的範圍也就是name指向的變量只能爲三個字母的範圍,把name付給str,str也只能指向三個字母範圍的變量,“abcd”超出了範圍。(其實改爲大小相同的“abd”也不行)
所以JAVA引入了一個字符串池的概念。就是說它會把所已知的字符串放入字符串池,如果你創建新字符串沒有使用new關鍵字,它首先會去字符串池找有沒有相同值的字符串,如果有的話就指向它;如果沒有的話就會創建新的空間。
所以在內存中應該爲:
二、父類與子類
舉個例子,定義一個Father類,一個Son類,測試
public class Father {
String name;
Father(String name) {
System.out.println("Father一個參數構造函數");
this.name = name;
}
void drive() {
System.out.println("騎自行車");
}
}
public class Son extends Father {
Son(String name) {
super(name);
System.out.println("Son一個參數構造函數");
this.name = name;
}
@Override
void drive() {
System.out.println("開寶馬");
}
void QQ() {
System.out.println("聊QQ");
}
}
public class Test {
public static void main(String[] args) {
Father son1 = new Son("小毛");
son1.drive();
}
}
結果爲:
Father一個參數構造函數
Son一個參數構造函數
開寶馬
內存分析:
son1爲Father類的指針,但new了一個Son類的對象,並把這個對象付給了son1。
當new一個子類對象(構造子類對象)的時候,需要先創建父類對象,也就是要調用父類的默認構造函數(無參構造函數),如果父類沒有無參構造函數也就是說他的無參構造函數被你重寫了,那麼子類也不能有無參的構造方法,並且在子類的有參構造方法中必須指明構造父類的構造方法(用Super關鍵字)。
在實際 的編程中,經常碰到兩個對象相同,一個改變了對於另一個是否影響的問題,實際中也常遇到這樣的問題,有的時候,可能一時發現不了問題,這個問題實際上就是對於Java中和C++中指針的認識沒有搞清楚。下面我來闡述我對Java中指針的理解。
儘管java並不使用顯示指針而且不允許編程者使用指針,可是訪問對象依然以來指針。一個對象佔用了從某個位置開始的一段內存空間,指針對於該對象而言只是一個保存了對象地址的變量,並且這個地址是對象的內存起始地址。在很多的語言裏,指針是一種變量類型的技術術語,在Java中避免了這個術語而是用引用代替了它。
用例子說明更爲明瞭。
比如定義了一個實體
Class Node{
Stirng name;
int age;
}
聲明:Node p=null,q=new Node("asd",10);
p=new Node("asd",10);
System.out.println(p==q);
理解了引用就不難理解,輸出是假,因爲我們比較的是兩個不同對象的引用變量,也就是,比較了兩個不同的引用(地址),而不是對象。因此爲了比較對象的內容,我們必須定義一個方法一次比較各個數據域。
public boolean equals(Node n)
{
return name.equals(n.name)&&age==n.age;
}
對象變量是對象的引用,這個現實有助於解釋爲什麼需要謹慎的使用賦值運算符。
聲明:Node node1=new Node("abc",20),node2=node1;
目的是創建對象node1,併爲他的兩個域賦值,然後創建對象node2,並將他的域初始化爲相同的值。這些對象應當是獨立的實體,因此對於他們中的任何一個的賦值都不會英系那個另一個的值,但是,在下面的賦值語句中:
node2.name="def";
node2.age=10;
之後打印System.out.println(node1.name+" "+node1.age+" "+node2.name+" "+node2.age );
結果竟是:def 10 def 10
這就充分的說明了這些變量名稱都是引用,相當於C++中的指針,指向的是同一個地址。
儘管Java中缺少顯示指針,但是Java比C/C++更依賴指針。只是這些對於編程者是透明的。