对象的引用传递

引用传递也称为传地址,指的是在方法的调用时,传递的参数是按引用传递,其实传递的是引用地址,也就是变量所对应的内存空间的地址。
方法调用时,若实际参数的引用——地址被传递给方法中相对应的形式参数,即形式参数和实际参数拥有相同的存储单元。在方法执行过程当中,对形式参数的操作实际上就是对实际参数的操作,因此形式参数值的改变将会影响实际参数值。

  如果想要进行引用传递分析,那么就得首先清楚两块内存。

1,堆内存:堆内存可以理解为一个对象的具体信息,每一个对象保存的只是属性信息,每一块堆内存的开辟都要通过关键字new完成。
2,栈内存:可以理解为一个整型变量(只能够保存一个数的值),其中保存的一块(只能保存一块)堆内存空间的内存地址数值。但为了方便理解,可以假设其保存的是对象的名字。

对象实例化分配内存操作
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();    //实例化一个对象
        book.title="java程序设计";  //设置类中title属性
        book.price=39.8;  //设置price属性
        book.talk();  //此处方法使用对象调用,不是直接调用
    }
}

该程序内部执行过程图


图片发自简书App

本程序如果没有实例化对象(未开辟堆内存空间的对象)进行类中的属性或方法的调用的时候会出现异常。所以对象在使用之前一定要开辟堆内存空间。
在程序中一个关键字new产生一个对象,就开辟一个内存空间,如果有两个关键字new,就表示开辟两个内存空间。此处的两个对象都占有各自的内存空间,彼此的操作应该是独立的。例:
Book bookA=new Book(); //实例化一个对象
Book bookB=new Book(); //实例化一个对象
用关键字new实例化两个对象bookA和bookB,两个对象各占用一个独立的空间,彼此独立。因此只要存在了关键字new,不管何种情况下,都表示要开辟新的内存空间。

-----------------------------分割线----------------------------

引用数据类型的传递
在java中,类本身就是引用数据类型,而对引用数据类型实际上就相当于其他语言的指针概念。
在Java中对于方法参数的传递,对象是传递引用,基本数据类型是传递值。

//在函数中传递基本数据类型
class hehe
{
    public static void change(int i,int j)  //交换参数的值
    {
        int temp=i;   //完成两个变量值的交换
        i=j;
        j=temp;
    }
    public static void main(String args[])
    {
        int a=3;
        int b=4;
        change(a,b);   //调用方法
        System.out.println("a="+a);
        System.out.println("b="+b);
    }
}

引用数据类型的传递并没有改变数据本身的值。因此参数中传递的是基本类型a和b的备份,在函数中交换的也是那份备份的值而不是数据本身。


传递引用数据类型

class hehe
{
    public static void change(int[] count)
    {
        count[0]=0;
        System.out.println("在方法内部count[0]="+count[0]);
    }
    public static void main(String args[])
    {
        int[] count={1,2,3,4,5};
        System.out.println("方法执行前count[0]="+count[0]);
        change(count);    //调用change方法
        System.out.println("方法执行后count[0]="+count[0]);
    }
}

在方法中传递引用数据类型int数组,实际上是其引用count的备份,他们都指向数组对象,在方法中可以数组对象内容。即:对复制的引用所调用的方法更改的是同一个对象。


对象的传递引用

class Person
{
    String name;
    int age;
}
class can
{
    public static void main(String args[])
    {
        Person p1=null;  //声明对象p1,此值为null,尚未实例化
        Person p2=null;  //声明对象p2,此值为null,尚未实例化
        p1=new Person();   //实例化对象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,相当于p1和p2都指向同一块内存。 最后一行代码将p1对象赋值为null,表示此对象不在引用任何内存空间。程序执行完此行以后实际上p1断开了对之前实例化对象的引用,而p2则继续指向p1原先的引用。可以理解为p2是通过p1实例化的,或者p1将自身的引用传递给了p2。


图片发自简书App

通过上图分析可以得出结论:所谓引用传递,指的是同一块堆内存空间,同时被多个栈内存所指向。引用传递的核心认识:不同的栈内存如果如果指向了同一块堆内存之中,所做的修改将影响所有的栈内存。


引用传递的使用

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程序设计";
        bookA.price=40;
        System.out.println("引用传递前对象bookA:");
        bookA.talk();
        bookB.title="java web开发";
        bookB.price=60;
        bookB=bookA;   //引用传递
        bookB.title="android开发";
        System.out.println("引用传递后对象bookA:");
        bookA.talk();
    }
}

执行bookA=bookB引用传递之前,bookA,bookB是用两个new关键字创建对象,有各自独立的堆内存,属性各不相同。但执行引用传递之后,bookA的引用传递给bookB,bookB不在指向原来的堆内存,而是和bookA指向同一块堆内存,所以对bookB属性值的设置就是对bookA相应的属性值修改,bookA.title(即bookB.title)="android开发";。
每一块栈内存只能够保存一块堆内存地址,但是反过来,一块堆内存可以同时被多个栈内存指向,在这种情况下,如果要改变某一个栈内存的保存地址内容,则必须先断开已有的堆内存地址连接,才可能指向新的堆内存空间,而如果一块堆内存空间内没有任何栈内存所指向的话,那么这块空间就成为垃圾,所有垃圾将等待被jvm中gc进行不定期收集,同时进行内存空间释放。

对象的引用

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());
    }
}

对象克隆
对象克隆指的是将一个对象进行内容的复制,如果想实现克隆操作,可以使用Object类之中定义分一个方法。
protected Object clone() throws CloneNotSupportedException;
如果要想正常地实现克隆操作,那么对象所在的类必须实现Cloneable接口,但这个接口里面没有定义任何方法,此接口属于标识接口,指的是一种能力的体现。

对象克隆
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);
    }
}

反射机制
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用他的任意一个方法,这种动态获取的信息以及动态调用对象的方法称为Java语言的反射机制。
Java反射机制主要提供以下功能:在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法。

根据对象找到类
import java.util.Date;
class hehe
{
    public static void main(String args[])throws Exception
    {
        Date date=new Date();
        Classcls=date.getClass();  //通过Java反射机制得到类的包名
        System.out.println(cls);
    }
}

getClass()方法是由Object类所定义的:public final Class<?>getClass(),此方法返回分对象类型为Class,而Class是反射操作的源头。但是如果要想取得Class类的实例化对象在Java中有三种方式。

方式一:利用Object类的getClass()方法,但是要求必须先产生指定类的对象才可以,几乎不用

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类提供的一个方法完成,在系统构架中使用。

Class<?>cls=Class.forName("java.util.Date");
System.out.println(cls);
根据对象找到类
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();  //实例化一个对象
        book.setTitle("Java开发实战经典");
        book.setPrice(79.8);
        System.out.println(book);
    }
}

传统工厂设计模式

*interface Book
{
    public String getTitle();
}
class MathBook implements Book
{
    @Override
    public String getTitle()
    {
        return"数学类图书";
    }
}
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");  //实例化一个对象
        System.out.println(book.getTitle());
    }
}

反射机制工厂模式

interface Book
{
    public String getTitle();
}
class MathBook implements Book
{
    @Override
    public String getTitle()
    {
        return"数学类图书";
    }
}
class A implements Book
{
    @Override
    public String getTitle()
    {
        return"计算机类图书";
    }
}
class Factory
{
    public static Book getInstance(String className)
    {
        Book book=null;
        try
        {
            book=(Book)Class.forName(className).newInstance();  //利用反射机制实例化一个对象
        }
        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());
    }
}

反射机制其他操作
如果类中没有提供无参构造方法,只提供了有参构造方法,则就必须明确的调用指定的构造方法才可以通过反射机制实例化对象。取得制定构造方法如下:
public Constructor<T>getConstructor(Class<?>…parameterTypes)throws NoSuchMethodException,SecurityException;
在Comstrucoor类中提供有一个实例化对象方法。
public T newInstance(Object…initargs)throws

InstantiationException,LLLegalAccessException,

LLLegalArgumentException,IncocationTargetException

调用构造方法取得实例化对象**
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开发实战金典",79.8);
        System.out.println(book);
    }
}

针对属性的操作明确给出要求,利用setter和getter设置和取得,而对于setter和getter要求必须按照指定格式编写。之所以存在这样的要求,是因为反射机制的原因。此时可以用Class类中的如下方法取得方法的对象。

public Method getMethod(String name,Class<?>…parameterTypes)
throws NoSuchMethodException,SecurityException;

取得了Method类的对象后可以利用以下方法进行方法的反射调用。

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开发实战金典";
        Classcls=Class.forName("Book");
        Object obj=cls.newInstance();   //产生对象可分配堆内存
        Method setMethod=cls.getMethod("set"+initcap(filedName),String.class);
        Method getMethod=cls.getMethod("get"+initcap(filedName));
        setMethod.invoke(obj,titleValue);  //对象.setTitle()调用
        System.out.println(getMethod.invoke(obj));   //对象.getTitle()调用
    }
    public static String initcap(String str)
    {
        return str.substring(0,1).toUpperCase()+str.substring(1);
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容

  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,139评论 11 349
  • 这篇文章是我之前翻阅了不少的书籍以及从网络上收集的一些资料的整理,因此不免有一些不准确的地方,同时不同JDK版本的...
    高广超阅读 15,488评论 3 83
  • Java8张图 11、字符串不变性 12、equals()方法、hashCode()方法的区别 13、...
    Miley_MOJIE阅读 3,681评论 0 11
  • 前言 很早前就想聊聊 MVC、MVVM,因为这是个非常有意思的话题,而且近些年来新的架构设计模式也层出不穷,除却 ...
    KavinZhou阅读 659评论 0 6
  • 看曹操南征北战,从中年战斗到老年,始终没有战胜蜀国,但是给自己的儿子打下稳固的江山,最后曹丕却统一了蜀国。司马懿没...
    一只羊1237阅读 305评论 0 0