美文网首页java学习之路
JavaGuide知识点整理——基础常见知识点(中)

JavaGuide知识点整理——基础常见知识点(中)

作者: 唯有努力不欺人丶 | 来源:发表于2022-06-24 17:56 被阅读0次

    面向对象基础

    面向对象和面向过程的区别

    两者的主要区别在于解决问题的方式不同:

    • 面向过程把解决问题的过程拆成一个个方法,通过一个个方法的执行解决问题。
    • 面向对象会先抽象出对象,然后用对象执行方法的方式解决问题。

    面向对象开发的程序一般更易维护,易复用,易扩展。

    题外话:面向过程性能一般比面向对象高。因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最主要因素的时候,比如单片机,嵌入式开发,linux/unix等一般采用面向过程开发。但是java性能差主要是因为他是半编译型语言。

    成员变量与局部变量的区别有哪些?

    • 语法形式:从语法形式上看,成员变量是属于类的,而局部变量是在代码块或者方法中定义的变量或者是方法的参数。成员变量可以被public,private,static等修饰符所修饰,而局部变量不能被访问控制修饰符以及static修饰,但是局部变量可以被final修饰。
    • 存储方式:从变量在内存中的存储方式来看,成员变量用static修饰的话那么这个成员变量是属于类的,如果没有static修饰,这个成员变量是属于实例的。而对象存在于堆内存中,局部变量存在于栈内存。
    • 生存时间:从变量在内存中的生存时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动生成,随着方法的调用结束而消亡。
    • 默认值:从变量是否有默认值来看,成员变量如果没有被赋初始值,则会自动以类型的默认值而赋值(final修饰的成员变量必须显式赋值),而局部变量则不会自动赋值。

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

    new 运算符。 new创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)。
    一个对象引用可以指向0个或者一个对象, 一个对象可以被n个引用指向。

    对象的相等和引用的相等

    • 对象的相等一般比较的是内存中存放的内容是否相等
    • 引用的相等一般比较的是他们纸箱的内存地址是否相等

    类的构造方法的作用是什么?

    构造方法是一种特殊的方法,主要作用是完成对象的初始化工作。

    如果一个类没有声明构造方法,该程序能正确执行么?

    可以的,因为一个类没有声明构造方法会有一个默认的不带参数的构造方法,如果我们自己添加了构造方法,java就不会再添加默认的无参构造方法了。
    我们一直在不知不觉的时候构造方法,这也就是我们new的时候对象后面加个括号(其实这就是在调用无参构造),如果我们需要重写构造方法,那么我们应该把无参的构造方法也写出来。

    构造方法有哪些特点?是否可以被重写?

    • 名字和类名相同。
    • 没有返回值,但不能用void声明构造函数。
    • 生成类的对象时自动执行,无需调用。

    构造方法不能被重写,但是可以重载。所以一个类可以有多个构造函数。

    面向对象的三大特征

    封装:把一个对象的状态信息隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性。
    就好像我们看不到空调的内部零件信息,但是可以用遥控器控制空调。
    继承:不同类型的对象,相互之间经常有一定数量的共同点。但是也有各自的特点,比如说一个班级所有同学都是人,细分还有男人,女人,再细分小明比较胖,小红比较瘦,小李比较聪明,小王力气大等。
    继承是使用已存在的类作为基础建立新类的技术。新的子类可以加新的数据或者新的功能,也可以使用父类的功能。但是java中父类必须全继承,而且只能单继承。
    通过使用继承,可以快速的创建新的类,可以提高代码的重用,程序的可维护性,节省大量创建类的时间,提高开发效率。
    多态:顾名思义表示一个对象具有多种的状态。具体表现为父类的引用指向子类的实例。
    多态的特点:

    • 对象类型和引用类型之间具有继承/实现的关系。
    • 引用类型变量发出的方法调用的到底是哪个类中的方法必须在程序运行期间才能确定。
    • 多态不能调用只在子类中不在父类中的方法。
    • 如果子类重写了父类的方法,真正执行的是之类覆盖后的方法,否则执行的是父类的方法。

    接口和抽象类有什么共同点和区别?

    共同点:

    • 都不能被实例化
    • 都可以包含抽接口象方法
    • 都可以有默认实现的方法(java8可以用default关键字在接口中定义默认方法)
      区别:
    • 接口主要用于对类的行为进行约束,实现了某接口就具有对应的行为。抽象类主要用于代码复用,强调的是所属关系。
    • 单继承,多实现。
    • 接口中成员变量只能是public static final类型的,不能被修改且必须有初始值。而抽象类的成员变量默认default,可以在子类中被重新定义,也可以被重新赋值。

    深拷贝和浅拷贝的区别,什么是引用拷贝?

    • 浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点)。不过如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址。也就是说拷贝对象和原对象共用同一个内部对象。
    • 深拷贝:深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。
      下面是关于深浅拷贝的代码实例:
    浅拷贝是true,深拷贝是false

    图中代码很明显能看出来,浅拷贝只把对象拷贝了一份,但是对象是引用还是一样的。而深拷贝则是把对象从里到外都拷贝了一份,所以内部对象也不一样了。
    而引用拷贝其实就是两个不同的引用指向同一个对象。


    引用拷贝
    浅拷贝
    深拷贝

    JAVA常见类

    Object

    Object类是一个特殊的类,是所有类的父类,它主要提供了以下11个方法:

           //native方法,用于返回当前运行时对象的Class对象。使用final关键字修饰
            public final native Class<?> getClass();
            //native方法,用于返回对象的哈希码,主要使用在哈希表中
            public native int hashCode();
            //用于比较两个对象的内存地址是否相等,String类对该方法进行了重写用于比较字符串是否相等。 
            public boolean equals(Object obj);
            //native方法,用于创建并返回当前对象的一份拷贝。注意要实现Cloneable接口才可以调用这个方法
            protected native Object clone() throws CloneNotSupportedException;
            //返回类的名字实例的哈希码的16进制字符串,建议子类要重写这个方法。
            public String toString();
            //native方法,并且不能重写,唤醒一个在此对象监视器上等待的线程(监视器相当于锁的概念)。如果有多个线程正在等待随机唤醒一个。
            public final native void notify();
            //native方法,并且不能重写,跟notify一样,区别就是唤醒此对象监视器上等待的所有线程。
            public final native void notifyAll();
            //native方法,并且不能重写,暂停线程的执行,注意:sleep方法不释放锁,wait方法释放锁。timeout是等待时间。
            public final native void wait(long timeout) throws InterruptedException;
            //多了nanos参数,这个参数表示额外时间(单位毫秒,范围0-999999),所以超时时间还要加上nanos毫秒
            public final void wait(long timeout,int nanos) throws InterruptedException;
            //跟之前两个wait方法一样,只不过这个会一直等待,没有超时时间这个概念。
             public final void wait() throws InterruptedException;
             //实例被垃圾回收器回收的时候触发的操作
             protected void finalize() throws Throwable{}
    

    == 和 equals()的区别:

    • 对于基本数据类型来说,== 比较的是值
    • 对于引用数据类型来说,== 比较的是对象的内存地址

    因为java只有值传递,所以对于==老说,不管是基本数据类型还是引用数据类型的变量,本质比较的都是值。只是引用类型变量存的值是对象的地址。

    • equals()不能用于判断基本数据类型的变量,只能判断两个对象是否相等。
      equals()方法是Object类中定义的,如果不重写的话等价于==。重写的话一般是比较对象的值是否相等。

    比如String类型的equals方法就是重写过的,变成比较值。new 两个一样值的字符串。==是false,equals判断就是true。
    hashCode()有什么用?
    hashCode()的作用是获取哈希码。也称为散列码,这个哈希码的作用是确定该对象在哈希表中的索引位置。
    hashCode()定义在JDK的Object类中,这意味着java中任何类都有hashCode()函数(需要注意hashCode()方法是本地方法,也就是用C或者C++实现的,该方法是将对象的内存地址转换为整数后返回)。
    散列表存储的是键值对,特点是:根据键快速的检索出对应的值,也其中就利用了散列码。
    为什么要有hashCode?
    这是为了比较方便的一种方式:首先hashCode是根据哈希算法算出来的一个值。而这个hashCode相等,不代表两个对象一定相等的。
    举个例子:对象是算术式,算法是加法。 也就是说和相等的算术式有可能是一样的,但是不一定一样。但是和不相等的算术式一定不一样。
    我们可以理解为hashCode是判断相等的第一步骤。通过判断hashCode我们可以排除很多无用的数据。从而再把筛选过一遍的数据去用equals()判断是不是相等。
    当然了我们把对象不同但是哈希值一样的情况称为哈希碰撞。月糟糕的哈希算法越容易碰撞。
    为什么重写equals()时必须重写hashCode()方法?
    因为规范中两个相等的对象hashCode值必须相等,也就是如果equals方法判断两个对象相等,那么hashCode也要相等。
    如果重写equals时没有重写hashCode,可能会导致hashCode值不相等,但是equals相等。

    String

    String,StringBuffer,StringBuilder的区别?

    • 可变性:
      String是不可变的
      StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder类中是用字符串数组保存字符串的,不过没有使用final和private关键字修饰,最关键是是AbstractStringBuilder类提供了修改字符串方法的啊append方法。
    • 线程安全性:
      String中的对象是不可变的,可以理解成常量,所以是线程安全的。
      AbstractStringBuilder是StringBuffer和StringBuilder的公共父类,但是StringBUffer对方法加了同步锁,所以也是线程安全的。StringBuilder并没有加同步锁,所以是线程不安全的。
    • 性能:
      每次对String类型进行改变都会生成新的String对象,然后将指针指向新对象。
      StringBuffer都会对它本身进行操作,而不是生成新的对象改变对象引用。
      StringBuilder和SringBuffer一样,但是因为没有加锁,锁能获得性能百分之10到百分之15的提升,但是要冒多线程不安全的风险。

    String为什么是不可变的?

    1. 保存字符串的数组被final修饰且为私有的,并且String类没有提供/暴露修改这个字符串的方法。
    2. String类被final修饰导致其不能被继承,进而避免了子类破坏String不可变。

    java9中将String的底层实现由char[]改成了byte[]

    字符串拼接用“+”还是StringBuilder?
    java本身不支持运算符重载,但是“+”和“+=”是专门为String类重载过的运算符。也是java中仅有的两个冲再过的元素符。
    使用这两个运算符会直接调用StringBuilder 调用 append()。拼接完成后调用toString()方法获得String对象。
    但是如果在循环中使用“+”拼接,会每次都创建一个StringBuilder对象。如果直接使用StringBuilder对象进行字符串拼接就不会存在这个问题了。

    String中的equals方法是被重写过的,比较的是值,而Object中的equals方法比较的是内存地址。

    字符串常量池:是jvm为了提高性能和减少内存消耗,针对字符串专门开辟的一块区域,主要避免字符串重复创建。

    String s = new String("abc"):这句话会创建一个或者两个对象。
    如果常量池不存在abc,那么会创建两个对象:

    • 堆中创建一个未初始化的String对象
    • 堆中创建字符串对象‘abc’并且在常量池中保存对应的引用
    • 调用构造方法对最开始创建的String对象赋值

    如果在常量池中存在abc了,只会创建一个对象。

    String.intern()方法有什么作用?
    String.intern()是一个native方法,其作用是将指定字符串对象的引用保存在字符串常量池中:

    1. 如果字符串常量池中保存了对应的字符串对象的引用,直接返回该引用。
    2. 如果字符常量池中没有保存对应的字符串对象的引用,那么会在常量池中创建一个指向该字符串对象的引用并返回。

    相关文章

      网友评论

        本文标题:JavaGuide知识点整理——基础常见知识点(中)

        本文链接:https://www.haomeiwen.com/subject/qizdvrtx.html