对象的引用传递

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

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

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

推荐阅读更多精彩内容