Java知识点补充

待办

目录

参考文章

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

  • 面向过程:性能高,降低系统开销,但是耦合性较大
  • 面向对象:低耦合、易复用、易维护、易扩展

Java性能低的原因:
Java代码并不是最终被CPU直接执行的代码,而是会被Java虚拟机编译成字节码文件。

跳转到目录

2、JVM、JDK、JRE

2.1 JVM

Java虚拟机是运行字节码文件的虚拟机。不针对某一特定的机器语言,因此具有跨平台性,无论在Windows、Linux还是macOS系统,都可以运行,使用相同的代码都会产生相同的结果。

字节码即为Java文件被编译后形成的 .class 文件,可以被JVM虚拟机识别。

Java程序从源代码到运行:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KuE6qiOL-1581489404391)(3B0F769412E841B8BC98F85A57CF82DD)]
注意:

  1. 在从 .class 到机器码这一步,JVM首先加载字节码文件,然后通过解释器逐行解释执行,因此执行速度相对较慢。
  2. 热点代码:被经常调用的方法和代码块。
  3. JIT编译器:属于运行时编译。当JIT编译器完成第一次编译之后,会将机器码保存下来,下次可以直接使用。

2.2 JDK

  • JDK:Java Development Kit,包含JRE,以及编译器和工具(javadoc和jdb),可以创建和编译Java程序。
  • JRE: Java Runtime Environment , Java程序运行环境,只能运行编译好的 .class 文件,不能创建和编译Java文件。

跳转到目录

3、构造器

3.1 构造器能否被重载?

父类的私有方法和构造方法不能被重写,但是可以重载

跳转到目录

4、重载和重写的区别

  • 重写:发生在子类中,子类对父类的方法体进行改变,方法名、参数类型、参数顺序、参数个数相同,返回值类型小于父类,抛出的异常范围小于父类,访问修饰符大于父类,如果父类方法修饰符是private则子类不能重写
  • 重载:在同一个类中,方法签名相同,参数类型,顺序和参数个数不同,方法返回值和修饰符可以不同

跳转到目录

5、Java 面向对象编程三大特性: 封装 继承 多态

  1. 封装:封装是把一个对象的属性进行私有化,同时提供一些可以被外界访问的方法,在方法中设置访问条件,从而可以保证数据的安全。

  2. 继承:可以基于已存在类定义新类,新类拥有已存在类的所有属性和方法,但是新类只能访问已存在类的非私有方法和属性;可以添加新的属性和方法,方便代码的复用。

  3. 多态:一个引用变量具有多个形态,具体指向的哪个形态的对象在运行期才能确定。

实现多态两个方法:

(1)继承,多个子类对同一个方法的重写
(2)接口,实现接口,并覆盖接口中的同一个方法

跳转到目录

6、字符串

6.1 String StringBuilder StringBuffer区别

  1. 可变性
    String是不可变,因为String类使用final关键字修饰字符数组来保存字符串;StringBuilder和StringBuffer都继承自AbstractStringBuilder,该类也是使用字符数组保存字符串,但是没有final修饰

AbstractStringBuilder.java

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value;
    int count;
    AbstractStringBuilder() {
    }
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
  1. 线程安全:String对象不可变,类似于常亮因此线程安全;StringBuilder和StringBuffer继承自AbstractStringBuilder,而StringBuffer对方法进行了加锁处理,所以线程安全,而StringBuilder未加锁所以线程不安全。

  2. 性能:每次改变String对象都会重新生成一个String对象;而StringBuffer和StringBuilder都是直接操作的对象本身,因此性能较高,而StringBuffer有加锁过程所以性能稍低于StringBuilder。

  3. 使用总结:

  • 操作少的数据:String
  • 多线程大数据:StringBuffer
  • 单线程大数据:StringBuilder

6.2 字符串拼接方法及性能比较

方法底层原理性能(循环50000次耗时)
“+”StringBuilder.append(),每次都会new一个StringBuilder5119
String.concat()使用数组(新建),新建String3623
StringBuilder使用数组(添加到原数组),线程不安全3
StringBuffer使用数组(添加到原数组),线程安全4
StringUtils.join()StringBuilder.append()25726

跳转到目录

7、包装类型

基本类型包装类型
byteByte
booleanBoolean
shortShort
charCharacter
intInteger
longLong
floatFloat
doubleDouble

7.1 自动装拆箱

  • 装箱:将基本类型的值包装成对应的引用类型
  • 拆箱:将包装类型转换成基本类型数据

跳转到目录

8、值得比较

8.1 == 与 equals比较

= =: 对于对象判断地址是否相等,对于基本类型判断的是值是否相等;
equals: 当没被重写时作用同 = =,如果被重写则判断对象的内容是否一样。

注意:

  1. String类中的equals被重写了,因为Object对象的equals方法判断的是地址;
  2. 在创建String对象时会先去内存常量池中查找是否存在值相同的对象,存在则直接使用,不存在才创建。

8.2 hashCode和equals

8.2.1 hashCode()介绍

hashCode() 是获取哈希码(散列值),返回一个 int 整数来确定对象在哈希表中的索引位置,以便于快速查找到该对象。

8.2.2 为什么重写equals时必须重写hashCode方法

例如,利用 HashSet 保存对象,首先加入一个对象时,会先调用 hashCode() 方法获取哈希值,然后与集合中其他对象的 哈希值进行比较,如果不存在相同值,会添加对象,如果相同则会再次调用 equals 方法比较对象的内容是否相同,如果不相同则加入当前对象,重新扩列到其他位置,如果相同则不会加入该对象。

  1. 如果两个对象相同,则其哈希值一定相同,且调用 equals 方法返回 true
  2. 如果两个对象哈希值相等,对象也不一定相等
  3. hashCode() 默认是对堆上的对象产生独特值,如果没有重写 hashCode() 则该 class 的两个对象无论如何都不会相等。

8.3 String.hashCode()

尽量不要在switch中使用String.hashCode()方法,因为存在冲突,例如Aa和BB的hashCode都是2112。
在这里插入图片描述
使用31(质数)作为乘数,为了尽可能使哈希链较短,提高查询速度,同时避免溢出。

跳转到目录

9、多线程

9.1 线程,进程,程序

  • 程序:含有指令和数据的文件,静态的代码。
  • 线程:比进程更小的执行单元。
  • 进程:程序的一次执行过程,系统运行程序的基本单元。

在一个操作系统内可以同时执行的多个程序为进程,进程之间相互独立;一个程序内能同时执行的多个程序段为线程。

9.2 线程的状态

创建状态 (new线程对象)—> 就绪状态 (调用 start方法,线程队列排队)运行状态 (获得CPU执行权,调用ru方法)—>阻塞状态 (调用 sleep, suspend,wait方法,发生阻塞,不能进入排队队列,只有引起阻塞原因被排除后,再次进入就绪)—> 死亡状态 (调用stop或run方法执行结束)

超时等待:在等待(就绪)状态基础上增加超时时间,例如:sleep( long mills ) 或 wait( long mills ) 方法,当超时时间到时,会返回到就绪状态。
在这里插入图片描述
在这里插入图片描述

9.3 多线程实现方式

  1. 继承 Thread 类,覆写run方法,但是不适合资源共享。
  2. 实现 Runnable 接口,重写run方法,start启动线程,适合资源共享,并且 避免了 Thread 方式单继承的缺点。
  3. 实现 Callable 接口,重写 call 方法,有返回值。

9.4 sleep和wait

  • sleep():Thread类的方法,释放资源不释放锁
  • wait():Object类的方法,释放资源释放锁,只能用于同步代码块中

9.5 死锁问题

锁的相互嵌套调用导致的,同步方法中嵌套同步代码块,同步代码块中调用同步函数。

9.6 synchronized 的底层怎么实现

待办1

跳转到目录

10、关键字

10.1 final

  • 修饰 变量 时:当修饰基本类型变量,该变量值初始化之后不能再改变;当修饰引用类型变量时,初始化之后不能再指向其他对象。
  • 修饰 时:该类不能被继承,final类中所有成员方法都会被隐式地指定为final。
  • 修饰 方法 时:锁定方法,该方法定义不能被修改(重写)。类中所有private方法被隐式地指定为final。

跳转到目录

11、异常

11.1 Java异常类层次结构图

在这里插入图片描述

  • Error(错误):是程序无法处理的错误,代码运行时 JVM(Java 虚拟机)出现的问题。
  • Exception(异常):是程序本身可以处理的异常。

异常和错误的区别:异常能被程序本身处理,错误是无法处理。

11.2 在以下4种特殊情况下,finally块不会被执行

  1. 在finally语句块第一行发生了异常。
  2. 在前面的代码中用了System.exit(int)已退出程序。 exit是带参函数 ;若该语句在异常语句之后,finally会执行
  3. 程序所在的线程死亡。
  4. 关闭CPU。

注意:

  1. System.exit(0)是正常退出程序,会将整个虚拟机里的内容都停掉清空
  2. System.exit(1)或者说非0表示非正常退出程序
  3. 当try语句和finally语句中都有return语句时,finally语句的内容将被执行,并且finally语句的返回值将会覆盖原始的返回值。

跳转到目录

12、序列化与反序列化

序列化是将Java对象转为字节序列,从而进行持久化或数据通信。反序列化是将字节序列转为Java对象。

12.1 序列化的底层原理

  1. 将对象实例相关的元数据输出
  2. 递归输出类的超类描述,直到不再有超类
  3. 从最顶层的超类开始输出类的实例对象的数据
  4. 从上之下递归输出对象的数据

12.2 序列化和反序列化步骤

被序列化的对象需要继承 Serializable 类。

  • 序列化步骤:
FileOutputStream fos = new FileOutputStream("d:/object.object");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(new User());
  • 反序列化步骤:
FileInputStream fis = new FileInutStream(“d:/object.object”);
ObjectInputStream ois = new ObjectInputStream(fis);
User user = (User)fis.readObject();

12.3 transient

static和transient修饰的成员变量不能被序列化,transient只能修饰变量,不能修饰类和方法。

跳转到目录

13、I/O流

13.1 获取键盘输入方法

  1. Scanner
Scanner input = new Scanner(System.in);
String s = input.nextLine();
input.close();
  1. BufferedReader (需要处理异常)
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String s = bufferedReader.readLine();

13.2 四个抽象类基类

Java I0流的40多个类都是从如下4个抽象类基类中派生出来的。

  • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

13.3 既然有了字节流,为什么还要有字符流?

  1. 避免乱码
  2. 提高性能,字符串是由Java虚拟机将字节转换得来,比较耗时

一般视屏,图片等媒体资源采用字节流,涉及的字符的文本等采用字符流。

13.4 BIO、NIO、AIO 有什么区别?

  • BIO:同步阻塞 I/O 模式(Blocking I/O),数据的读写阻塞在一个线程内完成。不用过多考虑系统的过载和限流。
  • NIO:同步非阻塞 I/O模式(Non-blocking),对应 java.nio 包,提供了 Channel,Selector,Buffer 等抽象,面向缓冲基于通道的 I/O 方法。NIO提供了 SocketChannel 和 ServerSocketChannel ,都支持阻塞和非阻塞。非阻塞模式对于低负载、低并发提升开发效率和更好的维护性。
  • AIO:异步非阻塞 I/O 模式(Asynchronous),异步NIO基于事件和回调机制,应用操作后直接返回,当后台处理完成后,操作系统会通知相关线程进行后续操作。

跳转到目录

14、集合

14.1 数组转成集合

14.1.1 Arrays.asList()使用指南

将一个数组转换为一个 List 集合。Arrays.asList() 将数组转换为集合后,底层其实还是数组。Arrays.asList() 方法返回的并不是 java.util.ArrayList ,而是 java.util.Arrays。 的一个内部类,这个内部类并没有实现集合的修改方法或者说并没有重写这些方法。

返回的集合不能使用集合相关方法,该方法只是转换接口,后台数据仍是数组。

使用注意事项:
传递的数组必须是对象数组,不能是基本类型(当传入一个原生数据类型数组时,Arrays.asList() 的真正得到的参数就不是数组中的元素,而是数组对象本身!)

14.1.2 数组转成集合正确操作

  1. 直接方法
public static <T> List<T> arrayToList(final T[] array){
     int len = array.length-1;
     
     final List<T> list = new ArrayList<>();
     
     while(len >= 0){
         list.add(array[len--]);
     }
     
     return list;
}
  1. 最简便方法(推荐)
List list = new ArrayList<>(Array.asList("a","b","c"));
  1. 使用 Java8 的Stream
Integer [] myArray = {1, 2, 3};
List list = Arrays.stream(myArray).collect(Collectors.toList());

//基本类型也可以实现转换
int [] myArray2 = {1, 2, 3};
List list2 = Arrays.stream(myArrays).collect(Collectors.toList())
  1. 使用 Guava
  • 对于不可变集合,使用 Immutable 类及其 of(参数) 方法与 copeOf(数组) 工厂方法
List <String> il1 = ImmutableList.of("string","elements");

String[] arr = {"string","elements"};
List <String> il2 = ImmutableList.copyOf(arr);
  • 对于可变集合,使用 Lists 类及其 newArrayList 工厂方法
List <String> l1 = Lists.newArrayList(collection);			// from collection
List <String> l2 = Lists.newArrayList(array);				// from array
List <String> l3 = Lists.newArrayList("string","elements");	// from varargs

  1. 使用Apache Commons Collections
List<String> list = new ArrayList<>();
CollectionUtils.addAll(list,str);

跳转到目录

15、循环/迭代器

15.1 不要在 foreach 循环里进行元素的 remove/add 操作

如果要对元素进行 remove 操作,可以使用迭代器的 remove 方法(如果并发需要对Iterator对象加锁),而不是集合类的 remove 方法。因为如果列表在任何时候从结构上修改创建迭代器之后,以任何方式除非迭代器自身的 remove/add 方法,迭代器都会抛出一个 ConcurrentModificationException,这就是单线程状态下产生的 fail-fast机制。

fail-fast机制:多线程下对 fail-fast 集合进行修改时,可能会抛出 ConcurrentModificationException,单线程下也会产生。

java.util 包下所有集合类都是 fail-fast ,而java.util.concurrent 包下所有类都是 fail-safe 的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值