面向物件的基礎篇_02
阿新 • • 發佈:2019-01-01
基礎篇_02的主要內容
- 1、引用傳遞和值傳遞
- 2、連結串列
一丶引用傳遞和值傳遞
1、基本型別和引用型別在記憶體中的儲存
|- Java中資料型別分為兩大類,【基本型別】和【物件型別】。
基本型別的變數儲存原始值,即它代表的值就是數值本身;
這裡的基本型別包括:byte,short,int,long,char,float,double,Boolean,returnAddress,etc...
引用型別的變數儲存引用值。
"引用值"指向記憶體空間的地址,代表了某個物件的引用,而不是物件本身,物件本身存放在這個引用值所表示的地址的位置。
引用型別包括:類型別,介面型別和陣列。
2、變數的基本型別和引用型別的區別
|- 變數也有兩種型別:【基本型別】和【引用型別】。
基本資料型別在宣告時系統就給它分配空間:
int a;
a=10;//正確,因為宣告a時就分配了空間
引用就不相同了,它宣告時只給變數分配了引用空間,而不分配資料空間:
Date date;
date=new Date();
date.getDate();
//執行例項化,開闢資料空間存放Date物件,然後把空間的首地址傳給“今天到的日期”變數
System.out.println(date.getDate());//輸出今天的日期
/*
*如果註釋掉date=new Date();就會有異常丟擲
*NullPointerException,意思就是說一個空的變數,一個空的棧記憶體。
*The local variable date may not have been initialized
*也就是說物件的資料空間沒有分配
*/
3、引用傳遞和值傳遞
引用傳遞是Java的核心問題,引用傳遞的操作的核心就是記憶體地址的傳遞。
這裡要用 【實際引數】 和 【形式引數】的概念來幫助理解,
值傳遞:
方法呼叫時,實際引數把它的值傳遞給對應的形式引數,函式接收的是原始值的一個copy,此時記憶體中存在兩個相等的基本型別,即實際引數和形式引數,後面方法中的操作都是對形參這個值的修改,不影響實際引數的值。
-
範例:
-
public class Test01 {
public static void change(int a){
a=10000;
}
public static void main(String[] args) {
int a=10;
System.out.println(a);//10
change(a);
System.out.println(a);//10
}
}
改變的是形式引數的值,結果不影響實際引數。
引用傳遞:也稱為傳地址。
方法呼叫時,實際引數的引用(地址,而不是引數的值)被傳遞給方法中相對應的形式引數,函式接收的是原始值的記憶體地址;在方法執行中,形參和實參內容相同,指向同一塊記憶體地址,方法執行中對引用的操作將會影響到實際物件。
-
範例:
-
public class Test02 {
public static void change(int []a){
a[0]=50;
}
public static void main(String[] args) {
int []a={10,20};
System.out.println(a[0]);//10
change(a);
System.out.println(a[0]);//50
}
}
這裡傳來的是地址,形式引數和實際引數有一樣的地址,改變了地址中的內容
通過以下三個Demo來說明作用
class Demo{
private int count = 10 ;
public void setCount(int count){
this.count = count ;
}
public int getCount(){
return this.count ;
}
};
public class Demo01 {
public static void main(String args[]){
Demo d1 = new Demo() ;
d1.setCount(100) ;
fun(d1) ;
System.out.println(d1.getCount()) ;
//輸出的結果為30
}
public static void fun(Demo d2){
d2.setCount(30) ;
}
};
public class Demo02 {
public static void main(String args[]){
String str = "hello" ;
fun(str) ;
System.out.println(str) ;
//輸出的結果是hello
}
public static void fun(String temp){
temp = "world" ;
}
};
字串的內容無法改變,改變的只是記憶體地址的指向。
class Demo{
private String str = "hello" ;
public void setStr(String str){
this.str = str ;
}
public String getStr(){
return this.str ;
}
};
public class Demo03 {
public static void main(String args[]){
Demo d1 = new Demo() ;
d1.setStr("world") ;
fun(d1) ;
System.out.println(d1.getStr()) ;
//最後的輸出結果是!!!
}
public static void fun(Demo d2){
d2.setStr("!!!") ;
}
};
二丶連結串列的認識(這裡是說一個指標域的)
1、連結串列是一個個結點連線起來的長鏈。
連結串列包括兩個部分
|-資料域:存放資料的區域。例如:姓名、地址、學號etc...
|-指標域:存放一個指標的區域。這個指標
class Node {
private String name ; // 保留節點名稱
private Node next ; // 儲存下一個節點的引用
public Node(String name){ //含有引數的構造器來儲存結點名稱
this.name = name ;
//this.name中的name是Node類中的name
//= name 是傳過來引數name
}
public String getName(){
return this.name ;
}
public void setNext(Node next){
this.next = next ;
}
public Node getNext(){
return this.next ;
}
}
public class Demo04 {
public static void main(String args[]){
Node na = new Node("A") ;
Node nb = new Node("B") ;
Node nc = new Node("C") ;
na.setNext(nb) ;//a的指標域指向b
nb.setNext(nc) ;//b的指標域指向c
}
}
在棧記憶體中存放的是na,nb,nc
在堆記憶體中存放的是name:A、B、C 和next:nb、nc、null
第一個結點A的指標域na指向的是nb,所以堆記憶體中存放的是結點A到的name和nb。同理可得。
在設定好了關係之後,只能通過遞迴的方式完成全部輸出。
public class Demo05 {
public static void main(String args[]){
Node na = new Node("車廂 A") ;
Node nb = new Node("車廂 B") ;
Node nc = new Node("車廂 C") ;
na.setNext(nb) ;
nb.setNext(nc) ;
print(na) ;
}
public static void print(Node node){
if(node != null){ // 避免空指向異常
System.out.println(node.getName()) ;
if(node.getNext() != null){
print(node.getNext()) ;
}
}
}
}
自動的完成增加和輸出的功能
需要考慮到以下的幾個問題:
每一個結點都要儲存起來。設定一個可以儲存節點關係的類:Node
需要一個新增結點,並按照結點次序連線起來的類:Link
第一個結點是頭結點,必須保留好根節點,private封裝起來
下面是一個連結串列的基本資料模型。
class Node {
private String name ; // 保留節點名稱
private Node next ; // 儲存下一個節點的引用
public Node(String name){
this.name = name ;
}
public String getName(){
return this.name ;
}
public void setNext(Node next){
this.next = next ;
}
public Node getNext(){
return this.next ;
}
public void addNode(Node newNode){//新增結點
if(this.next == null){
this.next = newNode ;
} else {
this.next.addNode(newNode) ; // 當前節點的下一個繼續往下判斷
}
}
public void printNode(){//輸出結點
System.out.println(this.name) ;
if(this.next != null){
this.next.printNode() ;
}
}
}
class Link { // 表示的是一個節點的操作類
private Node root ; // 表示根節點
public void add(String name){ // 增加新節點
Node newNode = new Node(name) ; // 新節點
if(this.root == null){ // 現在沒有根節點
this.root = newNode ; // 第一個節點是根節點
} else { // 應該排在最後
this.root.addNode(newNode) ; // 向後面排隊
}
}
public void print(){
this.root.printNode() ;//從根結點開始呼叫,這個函式的方法在類Node中
}
}
public class Test {
public static void main(String args[]){
Link link = new Link() ;
link.add("車廂 A") ;
link.add("車廂 B") ;
link.add("車廂 C") ;
link.add("車廂 D") ;
link.add("車廂 E") ;
link.print() ;
}
}