Java 基础知识点总结
Class 类和 Object 类
==============
1. Java 反射的基础是 Class 类,该类封装所有其他类的类型信息,并且在每个类加载后在堆区生成每个类的一个 Class<类名>实例,用于该类的实例化。
2. Java 中可以通过多种方式获取 Class 类型,比如 A.class,new A().getClass()方法以及 Class.forName("com.?.?.A" 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》开源 )方法。
3. Object 是所有类的父类,有着自己的一些私有方法,以及被所有类继承的 9 大方法。
知乎上有人讨论 Object 和 Class 类型谁先加载谁后加载,因为每个类都要继承 Object,但是又得先被加载到堆区,事实上,这个问题在 JVM 初始化时就解决了,没必要多想。
javac 和 java
==========
1. javac 是编译一个 java 文件的基本命令,通过不同参数可以完成各种配置,比如导入其他类,指定编译路径等。
2. java 是执行一个 java 文件的基本命令,通过参数配置可以以不同方式执行一个 java 程序或者是一个 jar 包。
3. javap 是一个 class 文件的反编译程序,可以获取 class 文件的反编译结果,甚至是 jvm 执行程序的每一步代码实现。
反射
==
1. Java 反射包 reflection 提供对 Class,Method,field,constructor1 等信息的封装类型。
2. 通过这些 api 可以轻易获得一个类的各种信息并且可以进行实例化,方法调用等。
类中的 private 参数可以通过 setaccessible 方法强制获取。
3. 反射的作用可谓是博大精深,JDK 动态代理生成代理类的字节码后,首先把这个类通过 defineclass 定义成一个类,然后用 class.for(name)会把该类加载到 jvm,之后我们就可以通过,A.class.GetMethod()获取其方法,然后通过 invoke 调用其方法,在调用这个方法时,实际上会通过被代理类的引用再去调用原方法。
枚举类
===
1. 枚举类继承 Enum 并且每个枚举类的实例都是唯一的。
2. 枚举类可以用于封装一组常量,取值从这组常量中取,比如一周的七天,一年的十二个月。
3. 枚举类的底层实现其实是语法糖,每个实例可以被转化成内部类。并且使用静态代码块进行初始化,同时保证内部成员变量不可变。
序列化
===
1. 序列化的类要实现 serializable 接口
transient 修饰符可以保证某个成员变量不被序列化
readObject 和 writeOject 来实现实例的写入和读取。
2. 事实上,一些拥有数组变量的类都会把数组设为 transient 修饰,这样的话不会对整个数组进行序列化,而是利用专门的方法将有数据的数组范围进行序列化,以便节省空间。
动态代理
====
1. jdk 自带的动态代理可以代理一个已经实现接口的类。
2. cglib 代理可以代理一个普通的类。
3. 动态代理的基本实现原理都是通过字节码框架动态生成字节码,并且在用 defineclass 加载类后,获取代理类的实例。
一般需要实现一个代理处理器,用来处理被代理类的前置操作和后置操作。在 JDK 动态代理中,这个类叫做 invocationHandler。
4. JDK 动态代理首先获取被代理类的方法,并且只获取在接口中声明的方法,生成代理类的字节码后,首先把这个类通过 defineclass 定义成一个类,然后把该类加载到 jvm,之后我们就可以通过,A.class.GetMethod()获取其方法,然后通过 invoke 调用其方法,在调用这个方法时,实际上会通过被代理类的引用再去调用原方法。
5. 而对于 cglib 动态代理,一般会把被代理类设为代理类的父类,然后获取被代理类中所有非 final 的方法,通过 asm 字节码框架生成代理类的字节码,这个代理类很神奇,他会保留原来的方法以及代理后的方法,通过方法数组的形式保存。
cglib 的动态代理需要实现一个 enhancer 和一个 interceptor,在 interceptor 中配置我们需要的代理内容。如果没有配置 interceptor,那么代理类会调用被代理类自己的方法,如果配置了 interceptor,则会使用代理类修饰过的方法。
多线程
===
这里先不讲 juc 包里的多线程类。juc 相关内容会在 Java 并发专题讲解。
1. 线程的实现可以通过继承 Thread 类和实现 Runable 接口 也可以使用线程池。callable 配合 future 可以实现线程中的数据获取。
2. Java 中的线程有 7 种状态,new runable running blocked waiting time_waiting terminate_
_blocked 是线程等待其他线程锁释放。waiting 是 wait 以后线程无限等待其他线程使用 notify 唤醒 time_wating 是有限时间地等待被唤醒,也可能是 sleep 固定时间。
3. Thread 的 join 是实例方法,比如 a.join(b),则说明 a 线程要等 b 线程运行完才会运行。
4. o.wait 方法会让持有该对象 o Java 开源项目【ali1024.coding.net/public/P7/Java/git】 的线程释放锁并且进入阻塞状态,notify 则是持有 o 锁对象的线程通知其他等待锁的线程获取锁。notify 方法并不会释放锁。注意这两个方法都只能在 synchronized 同步方法或同步块里使用。
5. synchronized 方法底层使用系统调用的 mutex 锁,开销较大,jvm 会为每个锁对象维护一个等待队列,让等待该对象锁的线程在这个队列中等待。当线程获取不到锁时则让线程阻塞,而其他检查 notify 以后则会通知任意一个线程,所以这个锁时非公平锁。
6. Thread.sleep(),Thread.interrupt()等方法都是类方法,表示当前调用该方法的线程的操作。
一个线程实例连续 start 两次会抛异常,这是因为线程 start 后会设置标识,如果再次 start 则判断为错误。
IO 流
===
1. IO 流也是 Java 中比较重要的一块,Java 中主要有字节流,字符流,文件等。其中文件也是通过流的方式打开,读取和写入的。
2. IO 流的很多接口都使用了装饰者模式,即将原类型通过传入装饰类构造函数的方式,增强原类型,以此获得像带有缓冲区的字节流,或者将字节流封装成字符流等等,其中需要注意的是编码问题,后者打印出来的结果可能是乱码哦。
3. IO 流与网络编程息息相关,一个 socket 接入后,我们可以获取它的输入流和输出流,以获取 TCP 数据包的内容,并且可以往数据报里写入内容,因为 TCP 协议也是按照流的方式进行传输的,实际上 TCP 会将这些数据进行分包处理,并且通过差错检验,超时重传,滑动窗口协议等方式,保证了 TCP 数据包的高效和可靠传输。
网络编程
====
承接 IO 流的内容
1. IO 流与网络编程息息相关,一个 socket 接入后,我们可以获取它的输入流和输出流,以获取 TCP 数据包的内容,并且可以往数据报里写入内容,因为 TCP 协议也是按照流的方式进行传输的,实际上 TCP 会将这些数据进行分包处理,并且通过差错检验,超时重传,滑动窗口协议等方式,保证了 TCP 数据包的高效和可靠传输。
2. 除了使用 socket 来获取 TCP 数据包外,还可以使用 UDP 的 DatagramPacket 来封装 UDP 数据包,因为 UDP 数据包的大小是确定的,所以不是使用流方式处理,而是需要事先定义他的长度,源端口和目标端口等信息。
3. 为了方便网络编程,Java 提供了一系列类型来支持网络编程的 api,比如 URL 类,InetAddress 类等。
Java8
=====
1. 接口中的默认方法,接口终于可以有方法实现了,使用注解即可标识出默认方法。
2. lambda 表达式实现了函数式编程,通过注解可以声明一个函数式接口,该接口中只能有一个方法,这个方法正是使用 lambda 表达式时会调用到的接口。
3. Option 类实现了非空检验
4. 各种 api 的更新,包括 chm,hashmap 的实现等
写在最后
为了这次面试,也收集了很多的面试题!
以下是部分面试题截图
评论