正文 Java基础 拾年之璐 V管理员 /2022年 /352 阅读 0706 ## 基础知识 ### JDK & JVM & JRE - **JVM**是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现,目的是使用相同的字节码,它们都会给出相同的结果。 - **JDK** 是是功能齐全的 Java SDK。它拥有 JRE 所拥有的一切,还有编译器(javac)和工具(如 javadoc 和 jdb)。它能够创建和编译程序。 - **JRE** 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件。但是,它不能用于创建新程序。 ### 静态方法为什么不能调用非静态成员? 1. 静态方法是属于类的,在类加载的时候就会分配内存,可以通过类名直接访问。而非静态成员属于实例对象,只有在对象实例化之后才存在,需要通过类的实例对象去访问。 2. 在类的非静态成员不存在的时候静态成员就已经存在了,此时调用在内存中还不存在的非静态成员,属于非法操作。 ### 基本类型和包装类型的区别? - 包装类型不赋值就是 `null` ,而基本类型有默认值且不是 `null`。 - 包装类型可用于泛型,而基本类型不可以。 - 基本数据类型的**局部变量存放在 Java 虚拟机栈中的局部变量表中**,基本数据类型的**成员变量(未被 `static` 修饰 )存放在 Java 虚拟机的堆中**。包装类型属于对象类型,我们知道几乎所有对象实例都存在于堆中。 - 相比于对象类型, 基本数据类型占用的空间非常小。 ### 包装类型的常量池技术了解么? Java 基本类型的包装类的大部分都实现了常量池技术。 `Byte`,`Short`,`Integer`,`Long` 这 4 种包装类默认创建了数值 **[-128,127]** 的相应类型的缓存数据 `Character` 创建了数值在 **[0,127]** 范围的缓存数据 `Boolean` 直接返回 `True` or `False`。 ### 构造方法有哪些特点?是否可被 override? 构造方法特点如下: - 名字与类名相同。 - 没有返回值,但不能用 void 声明构造函数。 - 生成类的对象时自动执行,无需调用。 构造方法不能被重写,但是**可以重载**,所以你可以看到一个类中有多个构造函数的情况。 ## 面向对象基础 ### 面向对象三大特征 - 封装 : 把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性。 - 继承 : 不同类型的对象,相互之间经常有一定数量的共同点。继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承,可以快速地创建新的类,可以提高代码的重用,程序的可维护性,节省大量创建新类的时间 ,提高我们的开发效率。 - 多态 : 表示一个对象具有多种的状态,具体表现为父类的引用指向子类的实例。 ### 接口和抽象类有什么共同点和区别? **共同点** - 都不能被实例化 - 都可以包含抽象方法 - 都可以有默认实现方法(接口1.8之后可以使用default定义) **区别** - 接口主要用于对类的行为进行约束,你实现了某个接口就具有了对应的行为。抽象类主要用于代码复用,强调的是所属关系 - 一个类只能继承一个类,但是可以实现多个接口。 - 接口中的成员变量只能是 `public static final` 类型的,不能被修改且必须有初始值,而抽象类的成员变量默认 default,可在子类中被重新定义,也可被重新赋值。 ## Java常见对象 ### 字符串拼接用“+” 还是 StringBuilder? Java 语言本身并不支持运算符重载,“+”和“+=”是专门为 String 类重载过的运算符,也是 Java 中仅有的两个重载过的元素符。不过,在循环内使用“+”进行字符串的拼接的话,存在比较明显的缺陷:**编译器不会创建单个 `StringBuilder` 以复用,会导致创建过多的 `StringBuilder` 对象**。 ### String 类型的变量和常量做“+”运算时发生了什么? **对于编译期可以确定值的字符串,也就是常量字符串 ,jvm 会将其存入字符串常量池。**并且,**字符串常量拼接得到的字符串常量在编译阶段就已经被存放字符串常量池**,这个得益于编译器的优化(常量折叠)。而引用的值在程序编译期是无法确定的,编译器无法对其进行优化。不过,字符串使用 `final` 关键字声明之后,可以让编译器当做常量来处理。 ## 泛型 ### Java 泛型了解么?什么是类型擦除? - **Java 泛型(generics)** 是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制 - Java 的泛型是伪泛型,这是因为 Java 在运行期间,所有的泛型信息都会被擦掉,这也就是通常所说类型擦除 。 - 泛型一般有三种使用方式: 泛型类、泛型接口、泛型方法。 ## 反射 反射之所以被称为框架的灵魂,主要是因为它赋予了我们在运行时分析类以及执行类中方法的能力。通过反射你可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性。 ### 反射机制优缺点 - **优点** : 可以让咱们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利 - **缺点** :让我们在运行时有了分析操作类的能力,这同样也增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。另外,反射的性能也要稍差点,不过,对于框架来说实际是影响不大的。 ### 反射的应用场景 像 Spring/Spring Boot、MyBatis 等等,**这些框架中大量使用了动态代理,而动态代理的实现也依赖反射。** **注解** 的实现也用到了反射 , 可以基于反射分析类,然后获取到类/属性/方法/方法的参数上的注解。你获取到注解之后,就可以做进一步的处理。 ## 注解 常见的解析方法有两种: - **编译期直接扫描** :编译器在编译 Java 代码的时候扫描对应的注解并处理,比如某个方法使用`@Override` 注解,编译器在编译的时候就会检测当前的方法是否重写了父类对应的方法。 - **运行期通过反射处理** :像框架中自带的注解(比如 Spring 框架的 `@Value` 、`@Component`)都是通过反射来进行处理的。 ## 异常 在 Java 中,所有的异常都有一个共同的祖先 `java.lang` 包中的 `Throwable` 类。`Throwable` 类有两个重要的子类: - **`Exception`** :程序本身可以处理的异常,可以通过 `catch` 来进行捕获。`Exception` 又可以分为 Checked Exception (受检查异常,必须处理) 和 Unchecked Exception (不受检查异常,可以不处理)。 - **`Error`** :`Error` 属于程序无法处理的错误 。例如Java 虚拟机运行错误(`Virtual MachineError`)、虚拟机内存不够错误(`OutOfMemoryError`)、类定义错误(`NoClassDefFoundError`)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。 ### 受检查异常和不受检查异常的区别 - Java 代码在编译过程中,如果**受检查异常**没有被 `catch`/`throw` 处理的话,就没办法通过编译常见的受检查异常有: IO 相关的异常、`ClassNotFoundException` 、`SQLException`... - 即使不处理**不受检查异常**也可以正常通过编译 , 例如:`NullPointerException`、`NumberFormatException`(字符串转换为数字)、`ArrayIndexOutOfBoundsException`(数组越界)、`ClassCastException`(类型转换错误)、`ArithmeticException`(算术错误)等 ### finally 中的代码一定会执行吗? 不一定 - 比如说 `finally` 之前虚拟机被终止运行的话,finally 中的代码就不会被执行 - 程序所在的线程死亡 - 关闭 CPU ## I/O ### Java 序列化中如果有些字段不想进行序列化,怎么办? 对于不想进行序列化的变量,使用 `transient` 关键字修饰。 `transient` 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 `transient` 修饰的变量值不会被持久化和恢复。 关于 `transient` 还有几点注意: - `transient` 只能修饰变量,不能修饰类和方法。 - `transient` 修饰的变量,在反序列化后变量值将会被置成类型的默认值。例如,如果是修饰 `int` 类型,那么反序列后结果就是 `0`。 - `static` 变量因为不属于任何对象(Object),所以无论有没有 `transient` 关键字修饰,均不会被序列化。 ### Java 中 IO 流分为几种? - 按照流的流向分,可以分为输入流和输出流; - 按照操作单元划分,可以划分为字节流和字符流; - 按照流的角色划分为节点流和处理流。 ### 既然有了字节流,为什么还要有字符流? 字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。 ### Java 中 3 种常见 IO 模型 - BIO : 同步阻塞IO - NIO : 多路复用IO - AIO : 异步IO 本文采用创作共用版权 CC BY-NC-SA 3.0 CN 许可协议,转载或复制请注明出处! -- 展开阅读全文 --