引用傳遞也稱為傳地址,指的是在方法的調(diào)用時啦辐,傳遞的參數(shù)是按引用傳遞卓嫂,其實(shí)傳遞的是引用地址慷暂,也就是變量所對應(yīng)的內(nèi)存空間的地址。
方法調(diào)用時命黔,若實(shí)際參數(shù)的引用——地址被傳遞給方法中相對應(yīng)的形式參數(shù)呜呐,即形式參數(shù)和實(shí)際參數(shù)擁有相同的存儲單元就斤。在方法執(zhí)行過程當(dāng)中悍募,對形式參數(shù)的操作實(shí)際上就是對實(shí)際參數(shù)的操作,因此形式參數(shù)值的改變將會影響實(shí)際參數(shù)值洋机。
如果想要進(jìn)行引用傳遞分析坠宴,那么就得首先清楚兩塊內(nèi)存。
1绷旗,堆內(nèi)存:堆內(nèi)存可以理解為一個對象的具體信息喜鼓,每一個對象保存的只是屬性信息,每一塊堆內(nèi)存的開辟都要通過關(guān)鍵字new完成衔肢。
2庄岖,棧內(nèi)存:可以理解為一個整型變量(只能夠保存一個數(shù)的值),其中保存的一塊(只能保存一塊)堆內(nèi)存空間的內(nèi)存地址數(shù)值角骤。但為了方便理解隅忿,可以假設(shè)其保存的是對象的名字。
對象實(shí)例化分配內(nèi)存操作
class Book
{
String title;
double price;
public void talk()
{
System.out.println("title:"+this.title);
System.out.println("price:"+this.price);
}
}
class hehe
{
public static void main(String args[])
{
Book book=null; //聲明對象
book =new Book(); //實(shí)例化一個對象
book.title="java程序設(shè)計(jì)"; //設(shè)置類中title屬性
book.price=39.8; //設(shè)置price屬性
book.talk(); //此處方法使用對象調(diào)用邦尊,不是直接調(diào)用
}
}
該程序內(nèi)部執(zhí)行過程圖
本程序如果沒有實(shí)例化對象(未開辟堆內(nèi)存空間的對象)進(jìn)行類中的屬性或方法的調(diào)用的時候會出現(xiàn)異常背桐。所以對象在使用之前一定要開辟堆內(nèi)存空間。
在程序中一個關(guān)鍵字new產(chǎn)生一個對象蝉揍,就開辟一個內(nèi)存空間链峭,如果有兩個關(guān)鍵字new,就表示開辟兩個內(nèi)存空間又沾。此處的兩個對象都占有各自的內(nèi)存空間弊仪,彼此的操作應(yīng)該是獨(dú)立的。例:
Book bookA=new Book(); //實(shí)例化一個對象
Book bookB=new Book(); //實(shí)例化一個對象
用關(guān)鍵字new實(shí)例化兩個對象bookA和bookB杖刷,兩個對象各占用一個獨(dú)立的空間撼短,彼此獨(dú)立。因此只要存在了關(guān)鍵字new挺勿,不管何種情況下曲横,都表示要開辟新的內(nèi)存空間。
-----------------------------分割線----------------------------
引用數(shù)據(jù)類型的傳遞
在java中,類本身就是引用數(shù)據(jù)類型禾嫉,而對引用數(shù)據(jù)類型實(shí)際上就相當(dāng)于其他語言的指針概念灾杰。
在Java中對于方法參數(shù)的傳遞,對象是傳遞引用熙参,基本數(shù)據(jù)類型是傳遞值艳吠。
//在函數(shù)中傳遞基本數(shù)據(jù)類型
class hehe
{
public static void change(int i,int j) //交換參數(shù)的值
{
int temp=i; //完成兩個變量值的交換
i=j;
j=temp;
}
public static void main(String args[])
{
int a=3;
int b=4;
change(a,b); //調(diào)用方法
System.out.println("a="+a);
System.out.println("b="+b);
}
}
引用數(shù)據(jù)類型的傳遞并沒有改變數(shù)據(jù)本身的值。因此參數(shù)中傳遞的是基本類型a和b的備份孽椰,在函數(shù)中交換的也是那份備份的值而不是數(shù)據(jù)本身昭娩。
傳遞引用數(shù)據(jù)類型
class hehe
{
public static void change(int[] count)
{
count[0]=0;
System.out.println("在方法內(nèi)部count[0]="+count[0]);
}
public static void main(String args[])
{
int[] count={1,2,3,4,5};
System.out.println("方法執(zhí)行前count[0]="+count[0]);
change(count); //調(diào)用change方法
System.out.println("方法執(zhí)行后count[0]="+count[0]);
}
}
在方法中傳遞引用數(shù)據(jù)類型int數(shù)組,實(shí)際上是其引用count的備份黍匾,他們都指向數(shù)組對象栏渺,在方法中可以數(shù)組對象內(nèi)容。即:對復(fù)制的引用所調(diào)用的方法更改的是同一個對象锐涯。
對象的傳遞引用
class Person
{
String name;
int age;
}
class can
{
public static void main(String args[])
{
Person p1=null; //聲明對象p1磕诊,此值為null,尚未實(shí)例化
Person p2=null; //聲明對象p2纹腌,此值為null霎终,尚未實(shí)例化
p1=new Person(); //實(shí)例化對象p1
p1.age=25;
p1.name="kimi";
p2=p1; //將p1引用的給p2
System.out.println("姓名,"+p2.name);
System.out.println("年齡升薯,"+p2.age);
p1=null;
}
}
將p1的引用賦給p2莱褒,相當(dāng)于p1和p2都指向同一塊內(nèi)存。 最后一行代碼將p1對象賦值為null涎劈,表示此對象不在引用任何內(nèi)存空間广凸。程序執(zhí)行完此行以后實(shí)際上p1斷開了對之前實(shí)例化對象的引用,而p2則繼續(xù)指向p1原先的引用责语∨谡希可以理解為p2是通過p1實(shí)例化的,或者p1將自身的引用傳遞給了p2坤候。
通過上圖分析可以得出結(jié)論:所謂引用傳遞胁赢,指的是同一塊堆內(nèi)存空間,同時被多個棧內(nèi)存所指向白筹。引用傳遞的核心認(rèn)識:不同的棧內(nèi)存如果如果指向了同一塊堆內(nèi)存之中智末,所做的修改將影響所有的棧內(nèi)存。
引用傳遞的使用
class Book
{
String title;
double price;
public void talk()
{
System.out.println("title:"+this.title);
System.out.println("price:"+this.price);
}
}
class hehe
{
public static void main(String args[])
{
Book bookA=new Book();
Book bookB=new Book();
bookA.title="java程序設(shè)計(jì)";
bookA.price=40;
System.out.println("引用傳遞前對象bookA:");
bookA.talk();
bookB.title="java web開發(fā)";
bookB.price=60;
bookB=bookA; //引用傳遞
bookB.title="android開發(fā)";
System.out.println("引用傳遞后對象bookA:");
bookA.talk();
}
}
執(zhí)行bookA=bookB引用傳遞之前徒河,bookA,bookB是用兩個new關(guān)鍵字創(chuàng)建對象系馆,有各自獨(dú)立的堆內(nèi)存,屬性各不相同顽照。但執(zhí)行引用傳遞之后由蘑,bookA的引用傳遞給bookB闽寡,bookB不在指向原來的堆內(nèi)存,而是和bookA指向同一塊堆內(nèi)存尼酿,所以對bookB屬性值的設(shè)置就是對bookA相應(yīng)的屬性值修改爷狈,bookA.title(即bookB.title)="android開發(fā)";裳擎。
每一塊棧內(nèi)存只能夠保存一塊堆內(nèi)存地址涎永,但是反過來,一塊堆內(nèi)存可以同時被多個棧內(nèi)存指向鹿响,在這種情況下羡微,如果要改變某一個棧內(nèi)存的保存地址內(nèi)容,則必須先斷開已有的堆內(nèi)存地址連接惶我,才可能指向新的堆內(nèi)存空間妈倔,而如果一塊堆內(nèi)存空間內(nèi)沒有任何棧內(nèi)存所指向的話,那么這塊空間就成為垃圾指孤,所有垃圾將等待被jvm中g(shù)c進(jìn)行不定期收集启涯,同時進(jìn)行內(nèi)存空間釋放贬堵。
對象的引用
class Person
{
private String no;
private String name;
private House house;
public Person(String no,String name)
{
this.no=no;
this.name=name;
}
public String getPersonInfo()
{
return "人的編號:"+this.no+"姓名:"+this.name;
}
public void setHouse(House house)
{
this.house=house; //引用傳遞
}
public House getHouse()
{
return this.house;
}
}
class House
{
private double area;
private String address;
private Person person;
public House(double area,String adderss)
{
this.area=area;
this.address=adderss;
}
public String getHouseInfo()
{
return "房子面積:"+this.area+"地址:"+this.address;
}
public void setPerson(Person person)
{
this.person=person; //引用傳遞
}
public Person getPerson()
{
return this.person;
}
}
class TestHouse
{
public static void main(String args[])
{
Person per=new Person("zs01","張三");
House ho=new House(88,"王府井百貨");
per.setHouse(ho); //人有一間房子
ho.setPerson(per); //房子屬于一個人
System.out.println(per.getPersonInfo());
System.out.println(per.getHouse().getHouseInfo());
System.out.println(ho.getPerson().getPersonInfo());
}
}
對象克隆
對象克隆指的是將一個對象進(jìn)行內(nèi)容的復(fù)制恃轩,如果想實(shí)現(xiàn)克隆操作,可以使用Object類之中定義分一個方法黎做。
protected Object clone() throws CloneNotSupportedException;
如果要想正常地實(shí)現(xiàn)克隆操作叉跛,那么對象所在的類必須實(shí)現(xiàn)Cloneable接口,但這個接口里面沒有定義任何方法蒸殿,此接口屬于標(biāo)識接口筷厘,指的是一種能力的體現(xiàn)。
對象克隆
class Book implements Cloneable
{
//可以被克隆
private String title;
private double price;
public Book(String title,double price)
{
this.title=title;
this.price=price;
}
public void setTitle(String title)
{
this.title=title;
}
public void setPrice(double price)
{
this.price=price;
}
public String getTitle()
{
return title;
}
public double getPrice()
{
return price;
}
@Override
protected Object clone() throws CloneNotSupportedException
{
//重新定義了一次clone()
return super.clone();
}
@Override
public String toString()
{
return"Book[title"+title+",price="+price+"]";
}
}
class TestCloneDemo
{
public static void main(String args[])throws Exception
{
Book bookA=new Book("java從入門到精通",79.8);
Book bookB=(Book) bookA.clone();//對象克隆
bookB.setPrice(100.8);
System.out.println(bookA);
System.out.println(bookB);
}
}
反射機(jī)制
Java反射機(jī)制是在運(yùn)行狀態(tài)中宏所,對于任意一個類酥艳,都能夠知道這個類的所有屬性和方法;對于任意一個對象爬骤,都能夠調(diào)用他的任意一個方法充石,這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法稱為Java語言的反射機(jī)制。
Java反射機(jī)制主要提供以下功能:在運(yùn)行時判斷任意一個對象所屬的類霞玄;在運(yùn)行時構(gòu)造任意一個類的對象骤铃;在運(yùn)行時判斷任意一個類所具有的成員變量和方法;在運(yùn)行時調(diào)用任意一個對象的方法坷剧。
根據(jù)對象找到類
import java.util.Date;
class hehe
{
public static void main(String args[])throws Exception
{
Date date=new Date();
Classcls=date.getClass(); //通過Java反射機(jī)制得到類的包名
System.out.println(cls);
}
}
getClass()方法是由Object類所定義的:public final Class<?>getClass()惰爬,此方法返回分對象類型為Class,而Class是反射操作的源頭惫企。但是如果要想取得Class類的實(shí)例化對象在Java中有三種方式撕瞧。
方式一:利用Object類的getClass()方法,但是要求必須先產(chǎn)生指定類的對象才可以,幾乎不用
Date date=new Date();
Class<?>cls=date.getClass();
System.out.println(cls);
方式二:利用"類.class"的形式取得Class類的對象丛版,在Hibernate上使用咨跌。
Class<?>cls=java.util.class;
System.out.println(cls);
方式三:利用Class類提供的一個方法完成,在系統(tǒng)構(gòu)架中使用硼婿。
Class<?>cls=Class.forName("java.util.Date");
System.out.println(cls);
根據(jù)對象找到類
class Book
{
private String title;
private double price;
public void setPrice(double price)
{
this.price=price;
}
public void setTitle(String title)
{
this.title=title;
}
@Override
public String toString()
{
return"圖書名稱"+this.title+",價格"+this.price;
}
}
class hehe
{
public static void main(String args[])throws Exception
{
Classcls=Class.forName("Book");
Book book=(Book)cls.newInstance(); //實(shí)例化一個對象
book.setTitle("Java開發(fā)實(shí)戰(zhàn)經(jīng)典");
book.setPrice(79.8);
System.out.println(book);
}
}
傳統(tǒng)工廠設(shè)計(jì)模式
*interface Book
{
public String getTitle();
}
class MathBook implements Book
{
@Override
public String getTitle()
{
return"數(shù)學(xué)類圖書";
}
}
class Factory
{
public static Book getInstance(String className)
{
if("mathbook".equals(className))
{
return new MathBook();
}
return null;
}
}
class hehe
{
public static void main(String args[])
{
Book book=Factory.getInstance("mathbook"); //實(shí)例化一個對象
System.out.println(book.getTitle());
}
}
反射機(jī)制工廠模式
interface Book
{
public String getTitle();
}
class MathBook implements Book
{
@Override
public String getTitle()
{
return"數(shù)學(xué)類圖書";
}
}
class A implements Book
{
@Override
public String getTitle()
{
return"計(jì)算機(jī)類圖書";
}
}
class Factory
{
public static Book getInstance(String className)
{
Book book=null;
try
{
book=(Book)Class.forName(className).newInstance(); //利用反射機(jī)制實(shí)例化一個對象
}
catch(Exception e)
{
e.printStackTrace();
}
return book;
}
}
class heheheh
{
public static void main(String args[])throws Exception
{
Book book=Factory.getInstance("A");
System.out.println(book.getTitle());
}
}
反射機(jī)制其他操作
如果類中沒有提供無參構(gòu)造方法锌半,只提供了有參構(gòu)造方法,則就必須明確的調(diào)用指定的構(gòu)造方法才可以通過反射機(jī)制實(shí)例化對象寇漫。取得制定構(gòu)造方法如下:
public Constructor<T>getConstructor(Class<?>…parameterTypes)throws NoSuchMethodException,SecurityException;
在Comstrucoor類中提供有一個實(shí)例化對象方法刊殉。
public T newInstance(Object…initargs)throws
InstantiationException,LLLegalAccessException,
LLLegalArgumentException,IncocationTargetException
調(diào)用構(gòu)造方法取得實(shí)例化對象**
import java.lang.reflect.Constructor;
class Book
{
private String title;
private double price;
public Book(String title,double price)
{
this.title=title;
this.price=price;
}
@Override
public String toString()
{
return"圖書名稱"+this.title+",圖書價格"+this.price;
}
}
class hehe
{
public static void main(String args[])throws Exception
{
Classcls=Class.forName("Book");
Constructorcons=cls.getConstructor(String.class,double.class);
Book book=(Book)cons.newInstance("Java開發(fā)實(shí)戰(zhàn)金典",79.8);
System.out.println(book);
}
}
針對屬性的操作明確給出要求,利用setter和getter設(shè)置和取得州胳,而對于setter和getter要求必須按照指定格式編寫记焊。之所以存在這樣的要求,是因?yàn)榉瓷錂C(jī)制的原因栓撞。此時可以用Class類中的如下方法取得方法的對象遍膜。
public Method getMethod(String name,Class<?>…parameterTypes)
throws NoSuchMethodException,SecurityException;
取得了Method類的對象后可以利用以下方法進(jìn)行方法的反射調(diào)用。
public Object invoke(Object obj,Object…args)throws LLLegalAccessException,
LLLegalArgumentException,Invocation TargetException;
setter瓤湘,getter的使用
import java.lang.reflect.Method;
class Book
{
private String title;
public void setTitle(String title)
{
this.title=title;
}
public String getTitle()
{
return title;
}
}
class hehe
{
public static void main(String args[])throws Exception
{
String filedName="title"; //u要操作的屬性
String titleValue="Java開發(fā)實(shí)戰(zhàn)金典";
Classcls=Class.forName("Book");
Object obj=cls.newInstance(); //產(chǎn)生對象可分配堆內(nèi)存
Method setMethod=cls.getMethod("set"+initcap(filedName),String.class);
Method getMethod=cls.getMethod("get"+initcap(filedName));
setMethod.invoke(obj,titleValue); //對象.setTitle()調(diào)用
System.out.println(getMethod.invoke(obj)); //對象.getTitle()調(diào)用
}
public static String initcap(String str)
{
return str.substring(0,1).toUpperCase()+str.substring(1);
}
}