zoukankan      html  css  js  c++  java
  • Java 值传递与对象拷贝

    值传递与对象拷贝

    值传递和引用传递

    调用一个有参函数的时候,会把实际参数传递给形式参数。但是,在程序语言中,这个传递过程中传递的两种情况,即值传递和引用传递

    两者区别

    1. 值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数
    2. 引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数

    java中的值传递

    1. java中只存在值传递,不存在引用传递

    2. 对于基本数据类型

      public class ParamTest
      {
      	public static void main(String[] args) 
          {
             ParamTest pt = new ParamTest();
             int i = 10;
             pt.pass(i);
             System.out.println("print in main , i is " + i);
          }
      
          public void pass(int j) 
          {
             j = 20;
             System.out.println("print in pass , j is " + j);
          }
      }
      /*
      	输出
      	print in pass , j is 20
          print in main , i is 10
      */
      

      由此可见,对于基本数据类型,是在栈中重新建立一个引用 j,使其值等于传递的参数值 i,改变 j 并不影响 i

    3. 对于引用数据类型

      • 示例一

        public static void main(String[] args) 
        {
        ParamTest pt = new ParamTest();
           User hello = new User();
        hello.setName("Hello");
           hello.setGender("Male");
        pt.pass(hello);
           System.out.println("print in main , user is " + hello);
        }
        public void pass(User user) 
        {
           user.setName("HelloChange");
           System.out.println("print in pass , user is " + user);
        }
        /*
        	输出
        	print in pass , user is User{name='HelloChange', gender='Male'}
        	print in main , user is User{name='HelloChange', gender='Male'}
        */
        
      • 示例二

        public static void main(String[] args) 
        {
           ParamTest pt = new ParamTest();
           User hello = new User();
           hello.setName("Hello");
           hello.setGender("Male");
           pt.pass(hello);
           System.out.println("print in main , user is " + hello);
        }
        public void pass(User user) 
        {
           user = new User();
           user.setName("HelloChange");
           user.setGender("Male");
           System.out.println("print in pass , user is " + user);
        }
        /*
         输出
         print in pass , user is User{name='HelloChange', gender='Male'}
         print in main , user is User{name='Hello', gender='Male'}
        */
        

        示例一中,调用 User 类方法改变了内存地址上对象的属性,但这并不是对传入参数本身的改变,实际上传入 hello 之后,在栈中建立 user 引用,且指向地址与 hello 相同,即指向同一对象,对对象的改变并不是对 hello 这个引用的改变,其值一直在栈中指向对象空间地址

        示例二中,传入 hello 之后,在栈中建立 user 引用并于 hello 等值,但通过 new 对象,使 user 引用指向了新的对象,即 user 在栈中的值改变了,而 hello 的值一直指向原对象并不受影响

    对象拷贝

    含义

    有时侯我们需要获得一个新对象,和已经存在的对象完全相同,但又必须相互独立,如果使用简单的赋值方式,实际上指向的还是同一内存地址上的对象,操作一个引用可能会影响另一个,这就需要对象拷贝来获取一个完全相同的新对象

    5种方式

    1. setter/getter

      new 新对象后用 set/get 方法设置属性

    2. 浅克隆

      • 被复制的类需要实现Clonenable接口(不实现的话在调用 clone 方法会抛出 CloneNotSupportedException 异常), 该接口为标记接口(不含任何方法)
      • 覆盖clone() 方法,访问修饰符设为 public。方法中调用 super.clone() 方法得到需要的复制对象
      class Address
      {
          private String address;
      
          public String getAddress()
          {
              return address;
          }
          public void setAddress(String address)
          {
              this.address = address;
          }
      }
      class Student implements Cloneable
      {
          private int number;
      
          private Address addr;
      
          public Address getAddr()
          {
              return addr;
          }
      
          public void setAddr(Address addr)
          {
              this.addr = addr;
          }
      
          public int getNumber()
          {
              return number;
          }
      
          public void setNumber(int number)
          {
              this.number = number;
          }
      
          @Override
          public Object clone()
          {
              Student stu = null;
              try
              {
                  stu = (Student) super.clone();   //浅复制
              }
              catch (CloneNotSupportedException e)
              {
                  e.printStackTrace();
              }
              return stu;
          }
      }
      public class Test
      {
      
          public static void main(String args[])
          {
      
              Address addr = new Address();
              addr.setAddress("杭州市");
              Student stu1 = new Student();
              stu1.setNumber(123);
              stu1.setAddr(addr);
      
              Student stu2 = (Student) stu1.clone();
      
              System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAddress());
              System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAddress());
      
              stu2.setNumber(124);
              addr.setAddress("西湖区");
      
              System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAddress());
              System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAddress());
          }
      }
      
      /*
      	输出
      	学生1:123,地址:杭州市
      	学生2:123,地址:杭州市
      	学生1:123,地址:西湖区
      	学生2:124,地址:西湖区
      */
      

      通过浅克隆获得新对象,其基本数据类型成员变量得到了复制,修改后不影响原对象

      若变量为引用数据类型,则只复制地址引用,还是指向相同地址,修改时会相互影响

      若变量为 String 类型,则拷贝其地址引用。但是在修改时,它会从字符串池中重新生成一个新的字符串,原有字符串对象保持不变

    3. 深克隆

      • 浅复制只是复制了addr 变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象
      • 为了达到真正的复制对象,而不是纯粹引用复制。我们需要将 Address 类可复制化,并且修改 clone 方法,代码如下:
      class Address implements Cloneable
      {
          private String address;
      
          public String getAddress()
          {
              return address;
          }
      
          public void setAddress(String address)
          {
              this.address = address;
          }
      
          @Override
          public Object clone()//Address 类可复制化
          {
              Address addr = null;
              try
              {
                  addr = (Address) super.clone();
              }
              catch (CloneNotSupportedException e)
              {
                  e.printStackTrace();
              }
              return addr;
          }
      }
      class Student implements Cloneable
      {
          private int number;
      
          private Address addr;
      
          public Address getAddr()
          {
              return addr;
          }
          public void setAddr(Address addr)
          {
              this.addr = addr;
          }
          public int getNumber()
          {
              return number;
          }
          public void setNumber(int number)
          {
              this.number = number;
          }
          @Override
          public Object clone()
          {
              Student stu = null;
              try
              {
                  stu = (Student) super.clone();   //浅复制
              }
              catch (CloneNotSupportedException e)
              {
                  e.printStackTrace();
              }
              stu.addr = (Address) addr.clone();   //引用数据类型变量深复制
              return stu;
          }
      }
      public class Test
      {
      
          public static void main(String args[])
          {
      
              Address addr = new Address();
              addr.setAddress("杭州市");
              Student stu1 = new Student();
              stu1.setNumber(123);
              stu1.setAddr(addr);
      
              Student stu2 = (Student) stu1.clone();
      
              System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAddress());
              System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAddress());
      
              stu2.setNumber(124);
              addr.setAddress("西湖区");
      
              System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAddress());
              System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAddress());
          }
      }
      /*
      	输出
      	学生1:123,地址:杭州市
      	学生2:123,地址:杭州市
      	学生1:123,地址:西湖区
      	学生2:124,地址:杭州市
      */
      
    4. 工具类

      org.apache.commons 组件 BeanUtils 和 PropertyUtils,静态方法 copyProperties(Object o1,Object o2)

    5. 序列化

      序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作

      class Professor implements Serializable
      {
          String name;
          int age;
          Professor(String name, int age)
          {
              this.name = name;
              this.age = age;
          }
      }
      class Student implements Serializable
      {
          String name;//常量对象
          int age;
          Professor p;//引用数据类型
          Student(String name, int age, Professor p)
          {
              this.name = name;
              this.age = age;
              this.p = p;
          }
          //深克隆
          public Object deepClone() throws IOException, ClassNotFoundException
          {
              //将对象写到流里
              ByteArrayOutputStream bo = new ByteArrayOutputStream();
              ObjectOutputStream oo = new ObjectOutputStream(bo);
              oo.writeObject(this);
              //从流里读出来
              ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
              ObjectInputStream oi = new ObjectInputStream(bi);
              return (oi.readObject());
          }
      }
      public class Test
      {
          public static void main(String[] args) throws IOException, ClassNotFoundException
          {
              Professor p = new Professor("wangwu", 50);
              Student s1 = new Student("zhangsan", 18, p);
              Student s2 = (Student) s1.deepClone();
              s2.p.name = "lisi";
              s2.p.age = 30;
              System.out.println("name=" + s1.p.name + "," + "age=" + s1.p.age); 
              //学生1的教授不改变
          }
      }
      
  • 相关阅读:
    在CentOS7上部署OpenStack 步骤详解
    Linux运维工程师工作手册
    Nginx+Keepalived实现Web服务器负载均衡
    Django Nginx+uwsgi 安装配置
    Docker-搭建Docker Registry
    centos7系统默认防火墙Firewall使用方法
    Shell脚本编写及常见面试题(二)
    Shell脚本编写及常见面试题(一)
    Linux之解决每次git pull/git push都需输入密码设置
    基于thinkphp5的Excel上传
  • 原文地址:https://www.cnblogs.com/pgjett/p/12323336.html
Copyright © 2011-2022 走看看