Java 问题01 ----- 面向对象

Java 问题01 ----- 面向对象,第1张

Java 问题01 ----- 面向对象 问题
    什么是面向对象 ?JDK,JRE,JVM == 和 equals 比较?哈希码的作用是什么?为什么要有hashCode?finalString ,StringBuffer,StringBuilder重载 和 重写的区别?接口和抽象类的区别?List 和 Set 的区别?ArraysList 和 linkedList 区别?HashMap 和 HashTable 有什么区别,其底层实现是什么?ConcurrentHashMap 原理什么是字节码?采用字节码的好处是什么?Java中的异常体系Java 类加载器有那些?双亲委托模型是什么?GC如何判断对象可以回收?
1 面向对象 1.1 什么是面向对象?

是一种处理问题的思想
面向对象注重事情有哪些参与者(对象),以及各自需要做什么 ,

面向过程注重事情的每一个步骤及顺序,强调的是过程

面向过程比较直接高效;面向对象更易复用,扩展和维护

封装

封装的意义:
在于明确标识出允许外部使用的所有成员函数和数据项
内部细节对外部透明,外部调用无需修改或者关心内部实现

继承

继承父类的方法,并作出自己的改变或扩展

子类共性的方法或者属性直接使用父类的,而不需要自己在定义,(提升代码复用性,不需要在写冗余的代码)
只需要扩展自己个性化的就好

多态

基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同。

前提:继承,方法重写

父类引用指向子类对象
多态中,编译看左边,运行看右边

父类类型 变量名 = new 子类对象;
变量名.方法名

无法调用子类特有功能

2 JDK JRE JVM



3 == 和 equals 比较

==对比的是栈中的值,基本数据类型是变量值,引用类型是堆中内存对象的地址

equals 不重写,默认采用的是object中的equals,也就是 ==

equals:object中默认也是采用==比较,通常会重写
Object

public boolean equals(Object obj) {
   return (this == obj);
}

String

public boolean equals(Object anObject) {
   if (this == anObject) {
       return true;
  }
   if (anObject instanceof 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) {
               if (v1[i] != v2[i])
                   return false;
               i++;
          }
           return true;
      }
  }
   return false;
}

上述代码可以看出,String类中被复写的equals()方法其实是比较两个字符串的内容。

public class StringDemo {
     public static void main(String args[]) {
         String str1 = "Hello";
         String str2 = new String("Hello");
         String str3 = str2; // 引用传递
         System.out.println(str1 == str2); // false
         System.out.println(str1 == str3); // false
         System.out.println(str2 == str3); // true
         System.out.println(str1.equals(str2)); // true
         System.out.println(str1.equals(str3)); // true
         System.out.println(str2.equals(str3)); // true
    }
}

4 hashCode 与 equals

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。

equals()用于判断两个对象是否相等

4.1 这个哈希码的作用是什么?

确定该对象在哈希表中的索引位置。可以快速找到所需要的对象

hashCode() 定义在JDK的Object.java中,Java中的任何类都包含有
hashCode() 函数。
散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用
到了散列码!(可以快速找到所需要的对象)

4.2 为什么要有hashCode ?

使hash容器能够高效工作

以“HashSet如何检查重复”为例子来说明为什么要有hashCode:
对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,看该位置是否有
值,如果没有、HashSet会假设对象没有重复出现。但是如果发现有值,这时会调用equals()方法来
检查两个对象是否真的相同。如果两者相同,HashSet就不会让其加入 *** 作成功。如果不同的话,就会
重新散列到其他位置。这样就大大减少了equals的次数,相应就大大提高了执行速度。

4.3 注意

    两个对象相等是 hashCode值相等的充分不必要条件
    如果两个对象相等,则hashcode一定也是相同的
    两个对象有相同的hashcode值,它们也不一定是相等的(哈希碰撞)

    equals( ) 相等是两个对象相等的充要条件

    两个对象相等,对两个对象分别调用equals方法都返回true

    因此,equals方法被覆盖过,则hashCode方法也必须被覆盖

    hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个
    对象无论如何都不会相等(即使这两个对象指向相同的数据)

5 final
    修饰类:表示类不可被继承修饰方法:表示方法不可被子类覆盖,但是可以重载修饰变量:表示变量一旦被赋值就不可以更改它的值。
6 String、StringBuffer、StringBuilder

String是final修饰的,不可变,每次 *** 作都会产生新的String对象

StringBuffer和StringBuilder都是在原对象上 *** 作

StringBuffer是线程安全的,StringBuilder线程不安全的

StringBuffer方法都是synchronized修饰的
性能:StringBuilder > StringBuffer > String

场景:经常需要改变字符串内容时使用后面两个
优先使用StringBuilder,多线程使用共享变量时使用StringBuffe

7 重载 和 重写的区别
重载:
发生在同一个类中,方法名必须相同,参数类型不同,个数不同,顺序不同;
方法返回值和修饰符可以不同,发生在编译时;
重写:(两同两小一大)
发生在父子类中,
1. 方法名,参数列表必须相同,
2. 返回值范围小于等于父类,
3. 抛出异常范围小于等于父类,
4. 修饰符范围大于等于父类;

但是如果父类修饰符为private则子类就不能重写该方法。

8 接口和抽象类的区别 ?

    抽象类是对类本质的抽象,是is a的关系。
    抽象类包含并实现子类的通用特性,将子类存在差异化的特性进行抽象,交由子类去实现。

    而接口 是对行为的抽象,是like a的关系。
    接口的核心是定义行为,即实现类可以做什么,至于实现类主体是谁,是如何实现的,接口并不关心。

抽象类可以存在普通成员函数,而接口中只能存在public abstract 方法。抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final 类型的。抽象类只能继承一个,接口可以实现多个。 接口的 设计目的? 是制定规则,可以强制要求不同的类具有相同行为,它只约束了行为的有无,但不对如何实现行为进行限制。 抽象类的设计目的?

代码复用,当不同的类具有某些相同的行为(记为行为集合A),
且其中一部分行为的实现方式一致时(A的非真子集,记为B),
可以让这些类都派生于一个抽象类,在这个抽象类中实现了B,避免了让所有的子类来实现B。
而A减B的部分,留给各个子类自己实现。
正是因为A-B这一部分没有实现,所以抽象类不允许实例化(否则当A调用B时,无法执行)

使用场景

当你关注一个事物的本质的时候,用抽象类;
当着重点是一个 *** 作的时候,用接口

抽象类的功能要远超过接口,但是,定义抽象类的代价高。
因为高级语言来说(从实际设计上来说也是)每个类只能继承一个类。在这个类中,你必须继承或编写出其所有子类的所有共性。
虽然接口在功能上会弱化许多,但是它只是针对一个动作的描述。而且你可以在一个类中同时实现多个接口。在设计阶段会降低难度

9 List 和 Set 的区别
    List : 有序,按对象进入的顺序保存对象,可重复,允许多个null 元素对象,
    可以使用Iterator取出所有元素,再逐一遍历,
    还可以使用get(int index) 获取指定下标的元素。Set:无序,不可重复,最多允许有一个 null 元素对象,
    取元素时只能用Iterator接口取得所有元素,再逐一遍历各个元素。

10 ArrayList 和 linkedList 区别

ArrayList:

    结构:基于动态数组,连续内存存储,适合下标访问(随机访问),扩容机制:因为数组长度定,超出长度存数据时需要新建数组,然后将老数组的数据拷贝到新数组,如果不是尾部插入数据还会涉及到元素的移动(插入位置以后的元素依次往后复制一份,插入新元素),使用尾插法并指定初始容量可以极大提升性能、甚至超过linkedList(需要创建大量的node对象,因为linkedList 每插入一个元素,都会创建一个node对象, 性能消耗主要在创建对象上)

linkedList:

    结构:基于链表,可以存储在分散的内存中,适合做数据插入及删除 *** 作,不适合查询:需要逐一遍历。

    注意:遍历linkedList必须使用iterator,不能使用for循环,
    如果用for循环的话每次for循环体内通过get(i)取得某一元素时都需要对list重新进行遍历,性能消耗极大。

    另外不要试图使用indexOf等返回元素索引,并利用其进行遍历,使用indexlOf对list进行了遍历,当结果为空时会遍历整个列表
    性能很低,要把linkedList里所有元素都遍历一遍,才知道这个元素是不存在的

导图


拓展

手动实现链表的增删改查
链表的查询:
1、根据节点位置查询该节点;
2、根据节点查询节点存放的位置

11 HashMap 和 HashTable 有什么区别? 其底层实现是什么?

区别 :
(1)HashMap方法没有synchronized修饰,线程不安全,HashTable线程安全;
(2)HashMap允许key和value为null,而HashTable不允许
2.底层实现:数组+链表实现
jdk8开始链表高度到8、数组长度超过64,链表转变为红黑树,元素以内部类Node节点存在

计算key的hash值,二次hash然后对数组长度取模,对应到数组下标,如果没有产生hash冲突(下标位置没有元素),则直接创建Node存入数组,如果产生hash冲突,先进行equal比较,相同则取代该元素,不同,则判断链表高度插入链表,链
表高度达到8,并且数组长度到64则转变为红黑树,长度低于6则将红黑树转回链表key为null,存在下标0的位置
扩容:

12 ConcurrentHashMap 原理

线程安全,效率更高

    数据结构:synchronized+CAS+Node+红黑树,Node的val和next都用volatile修饰,保证可见性查找,替换,赋值 *** 作都使用CAS(乐观锁,性能更高)锁:锁链表的head节点,不影响其他元素的读写,锁粒度更细,效率更高,扩容时(并发扩容),阻塞所有的读写 *** 作、并发扩容读 *** 作无锁:
    Node的val和next使用volatile修饰,读写线程对该变量互相可见(保证不会读到脏数据)
    数组用volatile修饰,保证扩容时被读线程感知

13 什么是字节码? 采用字节码的好处是什么? 什么是字节码

它是程序的一种表示,可以运行于Java虚拟机上。将程序抽象成字节码可以保证Java程序在各种设备上的运行

Java号称是一门“一次编译到处运行”的语言,从我们写的java文件到通过编译器编译成java字节码文件(.class文件),这个过程是java编译过程;而我们的java虚拟机执行的就是字节码文件。不论该字节码文件来自何方,由哪种编译器编译,甚至是手写字节码文件,只要符合java虚拟机的规范,那么它就能够执行该字节码文件

Java源代码---->编译器---->jvm可执行的Java字节码(即虚拟指令)---->jvm---->jvm中解释器----->机器可执行的二进制机器码---->程序运行。

采用字节码的好处:

Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以Java程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,
因此,Java程序无须重新编译便可在多种不同的计算机上运行

14 Java中的异常体系

    Java中的所有异常都来自顶级父类Throwable。
    Throwable下有两个子类Exception和Error。

    Error是程序无法处理的错误,一旦出现这个错误,则程序将被迫停止运行。

    Exception不会导致程序停止,又分为两个部分RunTimeException运行时异常和CheckedException检查异常。
    1)RunTimeException常常发生在程序运行过程中,会导致程序当前线程执行失败。
    2)CheckedException常常发生在程序编译过程中,会导致程序编译不通过。

15 Java 类加载器 JDK自带有三个类加载器:bootstrap ClassLoader、ExtClassLoader、AppClassLoader。
    BootStrapClassLoader是ExtClassLoader的父类加载器,默认负责加载%JAVA_HOME%lib下的jar包和
    class文件。ExtClassLoader是AppClassLoader的父类加载器,负责加载%JAVA_HOME%/lib/ext文件夹下的jar包和
    class类。AppClassLoader是自定义类加载器的父类,负责加载classpath下的类文件。系统类加载器,线程上下
    文加载器

继承ClassLoader实现自定义类加载器

16 双亲委托模型


main方法所在的类是由AppClassLoader加载的,(如果是自定义的类加载器,就从自定义的类加载器开始)
向上委派(查找缓存)
1. 不是直接加载,而是先查找缓存,先找自己的,没有则向上查找缓存(Ext),没有则继续往上
2. 如果找到则直接加载返回

向下查找(查找加载路径)
3. 如果没有则到加载路径查找,有则加载返回,没有则向下查找

双亲委派模型的好处:
1.主要是为了安全性,避免用户自己编写的类动态替换 Java的一些核心类,比如 String。(自己的String类不会被加载)
2.保证类不会重复加载,因为 JVM中区分不同类,不仅仅是根据类名,相同的 class文件被不
同的 ClassLoader加载就是不同的两个类

17 GC如何判断对象可以回收

1.引用计数法:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计
数为0时可以回收。

  引用计数法,可能会出现A 引用了 B,B 又引用了 A,这时候就算他们都不再使用了,但因为相互
  引用 计数器=1 永远无法被回收。

2.可达性分析法:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC
Roots 没有任何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5712798.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存