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

问题

  1. 什么是面向对象 ?
  2. JDK,JRE,JVM
  3. == 和 equals 比较?
  4. 哈希码的作用是什么?为什么要有hashCode?
  5. final
  6. String ,StringBuffer,StringBuilder
  7. 重载 和 重写的区别?
  8. 接口和抽象类的区别?
  9. List 和 Set 的区别?
  10. ArraysList 和 LinkedList 区别?
  11. HashMap 和 HashTable 有什么区别,其底层实现是什么?
  12. ConcurrentHashMap 原理
  13. 什么是字节码?采用字节码的好处是什么?
  14. Java中的异常体系
  15. Java 类加载器有那些?
  16. 双亲委托模型是什么?
  17. 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 注意

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

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

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

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

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

5 final

  1. 修饰类:表示类不可被继承
  2. 修饰方法:表示方法不可被子类覆盖,但是可以重载
  3. 修饰变量:表示变量一旦被赋值就不可以更改它的值。

6 String、StringBuffer、StringBuilder

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

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

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

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

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

7 重载 和 重写的区别

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

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

8 接口和抽象类的区别 ?

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

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

  • 抽象类可以存在普通成员函数,而接口中只能存在public abstract 方法。
  • 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final 类型的。
  • 抽象类只能继承一个,接口可以实现多个。

接口的 设计目的?

是制定规则,可以强制要求不同的类具有相同行为,它只约束了行为的有无,但不对如何实现行为进行限制。

抽象类的设计目的?

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

使用场景

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

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

9 List 和 Set 的区别

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

10 ArrayList 和 LinkedList 区别

ArrayList:

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

LinkedList:

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

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

  3. 另外不要试图使用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 原理

线程安全,效率更高

  1. 数据结构:synchronized+CAS+Node+红黑树,Node的val和next都用volatile修饰,保证可见性
  2. 查找,替换,赋值操作都使用CAS(乐观锁,性能更高)
  3. 锁:锁链表的head节点,不影响其他元素的读写,锁粒度更细,效率更高,扩容时(并发扩容),阻塞所有的读写操作、并发扩容
  4. 读操作无锁:
    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中的异常体系

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

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

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

15 Java 类加载器

JDK自带有三个类加载器:bootstrap ClassLoader、ExtClassLoader、AppClassLoader。
  1. BootStrapClassLoader是ExtClassLoader的父类加载器,默认负责加载%JAVA_HOME%lib下的jar包和
    class文件。
  2. ExtClassLoader是AppClassLoader的父类加载器,负责加载%JAVA_HOME%/lib/ext文件夹下的jar包和
    class类。
  3. 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 没有任何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值