java日常学习笔记(一)

        本人在学习过程走会遇到一些疑惑,在这里记录一些自己之前不太清楚的问题。如果在参考过程中有什么错误,可以指出来。


目录

一、为什么重写 equals() 时必须重写 hashCode() 方法?

二、深拷贝与浅拷贝

三、String s = new String("abc");这句话创建了几个字符串对象? 

四、java为什么只有值传递?


一、为什么重写 equals() 时必须重写 hashCode() 方法?

        在初学时,对这个问题充满了疑问,为什么一定要按照这个规则来呢?网上的参考资料大部分也都比较含糊其辞。我在参考了部分大佬的解释后,终于有了比较清晰的理解。

        是否一定要重写hashCode()方法主要是看对象用在何处。

1.不用做hash对象使用时

public class NotHashDemoTest {

    public static void main(String[] args) {
        //使用equals判断两个对象是否相等
        Person p1 = new Person("test", 100);
        Person p2 = new Person("test", 100);
        Person p3 = new Person("text", 200);
        System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());
        System.out.printf("p1.equals(p3) : %s; p1(%d) p3(%d)\n", p1.equals(p3), p1.hashCode(), p3.hashCode());
    }

    /**
     * @desc Person类。
     */
    private static class Person {
        int age;
        String name;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public String toString() {
            return name + " - " +age;
        }

        /**
         * @desc 覆盖equals方法
         */
        @Override
        public boolean equals(Object obj){
            if(obj == null){
                return false;
            }

            //如果是同一个对象返回true,反之返回false
            if(this == obj){
                return true;
            }

            //判断是否类型相同
            if(this.getClass() != obj.getClass()){
                return false;
            }

            Person person = (Person)obj;
            return name.equals(person.name) && age==person.age;
        }
    }
}

 结果:

p1.equals(p2) : true; p1(1360767589) p2(873415566)
p1.equals(p3) : false; p1(1360767589) p3(1007251739)

         可以看到,两个hash编码不相等的对象,但他们的equals是相等的。

2.用做hash对象使用

        用做hash对象使用,就是需要比较对象本身的hash值是否相等。比如:HashMap、HashSet等。

此处以hashSet举例:

public class HashDemoTest {

    public static void main(String[] args) {
        Person p1 = new Person("test", 100);
        Person p2 = new Person("test", 100);
        Person p3 = new Person("text", 200);

        HashSet hashSet = new HashSet<>();
        hashSet.addAll(Arrays.asList(p1, p2, p3));

        System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());
        hashSet.forEach(x -> System.out.print(x + ","));
    }

    /**
     * @desc Person类。
     */
    private static class Person {
        int age;
        String name;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public String toString() {
            return name + " - " +age;
        }

        /**
         * @desc 覆盖equals方法
         */
        @Override
        public boolean equals(Object obj){
            if(obj == null){
                return false;
            }

            //如果是同一个对象返回true,反之返回false
            if(this == obj){
                return true;
            }

            //判断是否类型相同
            if(this.getClass() != obj.getClass()){
                return false;
            }

            Person person = (Person)obj;
            return name.equals(person.name) && age==person.age;
        }
    }
}

结果为:

p1.equals(p2) : true; p1(1360767589) p2(873415566)
test - 100,text - 200,test - 100,

        可以看到,两个相等的对象还是添加进入了hashSet内。

当我们对hashCode()方法进行重写后:

public class HashDemoTest {

    public static void main(String[] args) {
        Person p1 = new Person("test", 100);
        Person p2 = new Person("test", 100);
        Person p3 = new Person("text", 200);

        HashSet hashSet = new HashSet<>();
        hashSet.addAll(Arrays.asList(p1, p2, p3));

        System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());
        hashSet.forEach(x -> System.out.print(x + ","));
    }

    /**
     * @desc Person类。
     */
    private static class Person {
        int age;
        String name;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public String toString() {
            return name + " - " +age;
        }


        @Override
        public int hashCode(){
            return name.hashCode() + age;
        }

        /**
         * @desc 覆盖equals方法
         */
        @Override
        public boolean equals(Object obj){
            if(obj == null){
                return false;
            }

            //如果是同一个对象返回true,反之返回false
            if(this == obj){
                return true;
            }

            //判断是否类型相同
            if(this.getClass() != obj.getClass()){
                return false;
            }

            Person person = (Person)obj;
            return name.equals(person.name) && age==person.age;
        }
    }
}

结果为:

p1.equals(p2) : true; p1(3556598) p2(3556598)
test - 100,text - 200,

        此时便可以看到,两个对象相等,未能加入到hashSet内。

总结: 

        是否需要重写hashCode()方法,需要根据实际情况来判定。

二、深拷贝与浅拷贝

        深拷贝与浅拷贝重点是:拷贝的对象与原对象是否共用一块内存。

浅拷贝:

        浅拷贝是拷贝的对象引用,而不是对象本身。

public class Address implements Cloneable{

    private String name;

    public Address(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public Address clone(){
        try {
            return (Address) super.clone();
        }catch (CloneNotSupportedException e){
            throw new RuntimeException();
        }
    }

}
public class Person implements Cloneable{

    private Address address;

    public Person(Address address) {
        this.address = address;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public Person clone(){
        try {
            return (Person) super.clone();
        }catch (CloneNotSupportedException e){
            throw new RuntimeException();
        }
    }
}
public class MainDemo {
    public static void main(String[] args) {
        Person person = new Person(new Address("123"));
        Person person1 = person.clone();
        System.out.println(person.getAddress() == person1.getAddress());
        System.out.println(person.getAddress().hashCode());
        System.out.println(person1.getAddress().hashCode());
    }
}
true
329611835
329611835

        结果为true。这说明,person和person1指向的地址是一致的。只是person1是拷贝的引用而已。

深拷贝:

         深拷贝的拷贝是会新建一个新的对象,指向新的内存地址。

public class Address implements Cloneable{

    private String name;

    public Address(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public Address clone(){
        try {
            return (Address) super.clone();
        }catch (CloneNotSupportedException e){
            throw new RuntimeException();
        }
    }

}
public class Person implements Cloneable{

    private Address address;

    public Person(Address address) {
        this.address = address;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public Person clone(){
        try {
            Person person = (Person) super.clone();
            person.setAddress(person.getAddress().clone());
            return person;
        }catch (CloneNotSupportedException e){
            throw new RuntimeException();
        }
    }
}
public class MainDemo {
    public static void main(String[] args) {
        Person person = new Person(new Address("123"));
        Person person1 = person.clone();
        System.out.println(person.getAddress() == person1.getAddress());
        System.out.println(person.getAddress().hashCode());
        System.out.println(person1.getAddress().hashCode());
    }
}
false
1468177767
434091818

        结果为false。说明现在两个对象已经不是共用一块内存了。

三、String s = new String("abc");这句话创建了几个字符串对象? 

         这个问题在我初始学习的时候,理解的不是很明白。工作之后,了解了JVM之后也不是理解的太清楚。最近好好钻研了一下。

        这个问题需要了解一点JVM中关于字符串常量池的知识,这里我大致介绍一下,以便能讲清问题。

        字符串常量池在JDK1.7之前,都是存放在方法区(也就是永久代,JDK1.8开始替换为元空间)之中。从1.7开始,因为永久代GC回收率太低,就将字符串常量池移动了java堆中。

        字符串常量池的作用:字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。

        在新建字符串对象时,如果已经有了对象,那么就直接返回其引用。

//创建字符串对象
//同时会将ab的引用保存到字符串常量池内
String a = "ab";
//字符串常量池内已经有了ab,直接返回其引用
String b = "ab";
System.out.println(a == b); //结果为true

         那么我们再看new String("ab")会创建几个对象。

        当已经有了字符串之后,那么只会创建一个字符串对象。

//已经有了ab对象的引用在字符串常量池内
String a = "ab";
//创建时只会创建一个字符串对象ab
String b = new String("ab");

         当没有时,那么就会创建两个对象。

String a = new String("ab");

 总结:

        在创建对象时,如果已经在字符串常量池内有了引用,那么就只会创建一个对象。如果没有,那么就会创建两个,一个创建在字符串常量池内,一个创建在堆内。

        简单来说:只要new一定会创建对象,String s = new String("abc")至少会创建一个,另外一个需要根据字符串常量池内是否已经创建来判断。

四、java为什么只有值传递?

        对于这个问题,我们可以通过代码的运行结果去查看。

基本数据类型:

public class SampleTest {
    public static void main(String[] args) {
        int x = 1;
        sysValue(x);
        System.out.println("实参:" + x);
    }

    public static void sysValue(int x) {
        x = 2;
        System.out.println("形参:" + x);
    }
}
形参:2
实参:1

        可以看出形参的改变并没有影响到实参。

字符串:

public class StringTest {

    public static void main(String[] args) {
        String name = "one";
        sysValue(name);
        System.out.println("实参:" + name);
    }

    public static void sysValue(String name) {
        name = "two";
        System.out.println("形参:" + name);
    }

}
形参:two
实参:one

        我们都知道字符串是不可变的,那么形参的改变不会影响实参是正常的。

对象:

        

public class UserTest {

    static class User {

        String name;

        int age;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }

    public static void main(String[] args) {
        User user = new User();
        user.setName("zhangsan");
        user.setAge(20);
        sysValue(user);
        System.out.println("实参:" + user.getName() + ":" + user.getAge());
    }

    public static void sysValue(User user) {
        user.setAge(21);
        System.out.println("形参:" + user.getName() + ":" + user.getAge());
    }

}
形参:zhangsan:21
实参:zhangsan:21

        当我们传递对象的时候,发现形参的改变影响了实参。我们再看一段代码。

public class UserTest {

    static class User {

        String name;

        int age;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }

    public static void main(String[] args) {
        User user = new User();
        user.setName("zhangsan");
        user.setAge(20);
        sysValue(user);
        System.out.println("实参:" + user.getName() + ":" + user.getAge());
    }

    public static void sysValue(User user) {
        user = new User();
        user.setName("wangwu");
        user.setAge(21);
        System.out.println("形参:" + user.getName() + ":" + user.getAge());
    }

}
形参:wangwu:21
实参:zhangsan:20

        我们在传递形参的方法内,将user指向了新的地址,之后新设置的值,也没有影响到实参。说明,对象在传递的过程中,传递的是对象引用的复制,传递后形参的改变并不会影响到实参。

总结:

        整体写的比较繁琐,最后总结成一句话也就是:简单变量(基本数据类型)是值传递;引用对象类型传递的是引用的复制,也是值的传递。所以说,java中只有值传递!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值