Java基础-面向对象总结(1)

本文主要梳理关于 Java面向对象的基础知识,希望对你有帮助

  • Java对象

目录

Java对象

Java创建对象有几种方式

创建一个对象用什么运算符? 对象实体与对象引用有何不同?

创建一个对象的步骤

Java对象都包含什么 ? 

new Object()对象占多少个字节?

对象的比较

对象的相等和引用相等的区别

==和 equals 的区别?

String类的equals方法源码有了解么 ? 

Comparator与Comparable有什么区别?

不可变对象


Java对象

Java创建对象有几种方式

创建对象总共有5种方式

  • 通过 new 关键字创建对象
//1.通过new关键字创建对象
Student s1 = new Student();
  • 通过 Class 类的 newInstance() 方法
//2.通过类.class的newInstance方法
Student s2 = Student.class.newInstance();

通过 Constructor 类的 newInstance 方法

//3.通过类构造方法的newInstance方法
Constructor<Student> constructor = Student.class.getConstructor();
Student s3 = constructor.newInstance();

利用 Clone 方法

//4.利用 Clone 方法-->实现cloneable接口并且重写clone方法
Student s4 = new Student();
try {
     Student s5 = (Student) s4.clone();
} catch (CloneNotSupportedException e) {
     e.printStackTrace();
}

反序列化

//5.通过序列化保存,反序列化创建对象 --> 记得要实现序列化接口
Student s6 = new Student();
try(ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:\\data.txt"))) {
    objectOutputStream.writeObject(s6);
}catch (IOException e) {
    e.printStackTrace();
}
//反序列化创建对象
try(ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:\\data.txt"))) {
    Student s7 = (Student) objectInputStream.readObject();
    System.out.println(s7);
} catch (Exception e) {
    e.printStackTrace();
}

创建一个对象用什么运算符? 对象实体与对象引用有何不同?

创建一个对象使用new关键字,对象实体是真正创建出来的对象(对象实体存放在堆内存),对象引用存放的是对象的地址(对象引用存放在栈内存中),对象引用指向对象.

  • 一个引用只可以指向1个对象或者0个对象.
  • 一个对象可以被多个引用所指向.

创建一个对象的步骤

  • 为对象分配内存空间
  • 初始化该对象
  • 给对象的引用赋值

Java对象都包含什么 ? 

首先 , 对象的组成有3个部分:对象头,实例数据,对齐填充字节, 对象头又包括 markword,指向class的指针,数组长度(如果不是数组的话就没有)

  1. 对象头
  • markword

如果jvm是32位,则Mark Word是32bit;如果64位,则Mark Word是64bit。Mark Word结构如下:

在这里插入图片描述

 Mark Workd存储的是对象自身运行时的数据,比如:锁标志位;是否偏向锁;GC分代年龄;对象的hashCode;获取到该锁的线程的线程ID;偏向时间戳(Epoch)等等。

  • Klass Word 类型指针

如果jvm是32bit,则Klass Word是32bit;如果jvm是64bit,则Klass Word是64bit。
Java对象存放在堆中,但其类信息存放在方法区中,所以Klass word指向该对象的类信息。

JVM通过这个指针来确定该对象是哪个类的实例

  • 数组长度
如果对 象是一个Java 数组,那在对象头中还必须有一块用于记录数组长度的数据

如果jvm是32bit,则数组长度是32bit;如果jvm是64bit,则数组长度是64bit

    2. 实例数据

来实例数据部分是对象真正存储的有效信息,即我们在程序代码里面所定义的各种类型的字
段内容,无论是从父类继承下来的,还是在子类中定义的字段都必须记录起来

    3. 对其填充字节

jvm要求Java对象占用内存的大小是8byte的倍数,因此需要把对象的大小补齐至8byte倍数。

另外 , 32位系统和64位系统不同区域占用空间大小的区别,因为在64位平台的HotSpot中使用32位指针(实际存储用64位),内存使用会多出1.5倍左右,使用较大指针在主内存和缓存之间移动数据,占用较大宽带,同时GC也会承受较大压力

但是现在假设一个场景,公司现在项目部署的机器是32位的,你们老板要让你将项目迁移到64位的系统上,但是又因为64位系统比32位系统要多出更多占用空间,怎么办,因为正常来说我们是不需要这一部分多余空间的,所以jvm已经帮你考虑好了,那就是指针压缩。

指针压缩

-XX:+UseCompressedOops 这个参数就是JVM提供给你的解决方案,可以压缩指针,将占用的空间压缩为原来的一半,起到节约空间的作用,classpointer参数大小就受到其影响。


new Object()对象占多少个字节?

64位系统下 :

  • 在开启指针压缩的情况下,markword占用8字节,classpoint占用4字节,Interface data无数据,总共是12字节,由于对象需要为8的整数倍,Padding会补充4个字节,总共占用16字节的存储空间。
  • 在没有指针压缩的情况下,markword占用8字节,classpoint占用8字节,Interface data无数据,总共是16字节。

对象的比较

对象的相等和引用相等的区别

对象相等指的是内存中存放的内容是否相等.

引用相等是指它们指向的内容地址是否相等.

==和 equals 的区别?

先说说 ==  ,对于==来说比较基本数据类型和引用数据类型有不同

  • 基本数据类型 : 比较的是
  • 引用类型 : 比较的是对象的内存地址

根本原因就是 Java都是值传递,不管是基本数据类型还是引用数据类型都比较的都是值,基本数据类型的值就是值,而引用数据类型是内存地址

再说说equals方法 ,equals方法只能用于判断两个对象是否相等,不能用来判断基本数据类型,另外equals方法来源于Object类,又因为Object类是所有类的父类,所以,所有类都有equals方法,如果没有重写,默认就是Object类的equals方法.

//默认的equals方法
public boolean equals(java.lang.Object obj) {
    return (this==obj);//【这里比对的是对象的内存地址】
}

对于equals有两种情况 :

  • 没有重写equals方法, 与==一样,默认是Object的equals方法
  • 重写了equals方法, 比较的对象的内容,相等返回true

String类的equals方法源码有了解么 ? 

对于String类就重写了equals方法,所以我们一般比较字符串的时候,比较的是两个字符串的内容

public boolean equals(Object anObject) {
    if (this == anObject) {//先比较两个字符串的内存地址是否相同
        return true;//相同返回true
    }
    if (anObject instanceof String) {//看该字符串是否是String类型
        String anotherString = (String) anObject;
        int n = value.length;
        if (n == anotherString.value.length) {//如果字符串长度相等,转化成字符数组,一一比较字符
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                //发现字符不相同,直接返回false
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    //长度不同直接返回false
    return false;
}

Comparator与Comparable有什么区别?

Comparable 和 Comparator 都是用来实现元素排序的,它们二者的区别如下:

  • Comparable 是“比较”的意思,而 Comparator 是“比较器”的意思;
  • Comparable 是通过重写 compareTo 方法实现排序的,而 Comparator 是通过重写 compare 方法实现排序的;
  • Comparable 必须由自定义类内部实现排序方法,而 Comparator 是外部定义并实现排序的。

总结二者的区别:Comparable 可以看作是“对内”进行排序接口,而 通过 Comparator 接口可以实现和原有类的解耦,在不修改原有类的情况下实现排序功能,所以 Comparator 可以看作是“对外”提供排序的接口。

@Data
public class Person implements Comparable<Person> {
    private int id;
    private int age;
    private String name;

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

    /**
     * 重写 compareTo 方法来实现自定义排序规则
     * compareTo 方法接收的参数 p 是要对比的对象,排序规则是用当前对象和要对比的对象进行比较,
     * 然后返回一个 int 类型的值。正序从小到大的排序规则是:使用当前的对象值减去要对比对象的值;
     * 而倒序从大到小的排序规则刚好相反:是用对比对象的值减去当前对象的值。
     * @param p
     * @return
     */
    @Override
    public int compareTo(Person p) {
        return p.getAge() - this.getAge();
    }
}
@Data
public class Person2 {
    private int id;
    private int age;
    private String name;

    public Person2(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }
}
//实现一个比较器 --> “对外”提供排序的接口。
class PersonComparator implements java.util.Comparator<Person> {

    @Override
    public int compare(Person o1, Person o2) {
        return o1.getAge() - o2.getAge();
    }
}

不可变对象

怎么实现一个不可变对象

如果要实现 immutable 的类,我们需要做到:

  1. 将class自身声明为final,这个类就不能被继承(别人就不能扩展来绕过限制)
  2. 将所有成员变量定义为 private 和final,并且不要实现setter方法
  3. 如果要修改类的状态,必须返回一个新的对象。
  4. 通常构造对象的时候,成员变量使用深拷贝来初始化,而不是直接赋值,(无法确定输入对象不被其他人修改) 
  5. 如果确实需要实现getter方法,或者其他可能返回内部状态的方法,使用 copy-on-write原则,创建私有的copy.
  6. 对于getter方法,和setter方法,建议做还是确定有需要的时候再去实现

不可变的好处是什么 ? 

对于String来说

  • 安全性

String 是 Java 中最基础也是最长使用的类,经常用于存储一些敏感信息,例如用户名、密码、网络连接等。因此,String 类的安全性对于整个应用程序至关重要。字符串容易被修改

  • 节省空间——字符串常量池

通过引入字符串常量池来节省空间,内容相同的字符串可以使用相同的字符串,可以节省内存空间,如果String可以改变,并且这个对象被其他多个地方所引用,这个字符串被修改,所有的地方都会被修改

  • 线程安全

String 对象是不可修改的,如果线程尝试修改 String 对象,会创建新的 String,所以不存在并发修改同一个对象的问题。

  • 性能

String 被广泛应用于 HashMap、HashSet 等哈希类中,当对这些哈希类进行操作时,例如 HashMap 的 get/put,hashCode 会被频繁调用。

由于不可变性,String 的 hashCode 只需要计算1次后就可以缓存起来,因此在哈希类中使用 String 对象可以提升性能。


能否创建一个包含可变对象的不可变对象

不要共享可变对象的引用就可以了,如果需要变化时,就返回原对象的一个拷贝

参考 :

String 为什么不可变?不可变有什么好处?_string为什么不可变_程序员囧辉的博客-CSDN博客

能否创建一个包含可变对象的不可变对象?_秋风清,秋月明。落日夕阳一片红的博客-CSDN博客

元素排序Comparable和Comparator有什么区别? - 掘金

Java基础常见面试题总结(中) | JavaGuide(Java面试 + 学习指南)

聊聊Java中的不可变对象 | Java程序员进阶之路

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值