目录
Java 泛型了解么?什么是类型擦除?介绍一下常用的通配符?
Checked Exception 和 Unchecked Exception 有什么区别?
如何使用 try-with-resources 代替try-catch-finally?(**)
基础
语言类型了解
编译型 :编译型语言 (opens new window) 会通过编译器 (opens new window)将源代码一次性翻译成可被该平台执行的机器码。一般情况下,编译语言的执行速度比较快,开发效率比较低。常见的编译性语言有 C、C++、Go、Rust 等等。
解释型 :解释型语言 (opens new window)会通过解释器 (opens new window)一句一句的将代码解释(interpret)为机器代码后再执行。解释型语言开发效率比较快,执行速度比较慢。常见的解释性语言有 Python、JavaScript、PHP 等等。
java是编译与解释并存
.class转机器码的时候,JVM类加载器会加载字节码文件,然后逐句解释,效率慢,后来引入JIT(just-in-time compilation) 编译器,而 JIT 属于运行时编译,当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。jdk9中引入了新的编译模式AOT(Ahead of Time Compilation),避免了JIT预热的开销,支持分层编译和AOT协作使用,但AOT的编译质量不如JIT
Java 和 C++ 的区别?
Java 和C++ 都是面向对象的语言,都支持封装、继承和多态,但是,它们还是有挺多不相同的地方:
- Java 不提供指针来直接访问内存,程序内存更加安全
- Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。
- Java 有自动内存管理垃圾回收机制(GC),不需要程序员手动释放无用内存。
- C ++同时支持方法重载和操作符重载,但是 Java 只支持方法重载(操作符重载增加了复杂性,这与 Java 最初的设计思想不符)。
重载和重写的区别
(重载overload:同名方法,多种实现方式;重写override:子承父业并改动)
重载:
重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理
重写:
- 重写就是当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法,重写发生在运行期,是子类对父类的允许访问的方法的实现过程进行重新编写(@Override)
- 方法名、参数列表必须相同,子类方法返回值类型应比父类方法返回值类型更小或相等,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类。
- 如果父类方法访问修饰符为
private/final/static
则子类就不能重写该方法,但是被static
修饰的方法能够被再次声明。 - 构造方法无法被重写
(注意:存在@Override注解,不存在@Overload注解,不要弄混)
基本类型(6+1+1)
基本类型 | 数据类型 | 封装类 | 位数 | 字节 | 默认值 | 范围 |
byte | 数字类型(整数型) | Byte | 8 | 1 | 0 | -128 ~ 127 |
short | 数字类型(整数型) | Short | 16 | 2 | 0 | -32768 ~ 32767 |
int | 数字类型(整数型) | Integer | 32 | 4 | 0 | -2147483648 ~ 2147483647 |
long | 数字类型(整数型) | Long | 64 | 8 | 0L | -9223372036854775808 ~ 9223372036854775807 |
float | 数字类型(浮点型) | Float | 32 | 4 | 0f | 1.4E-45 ~ 3.4028235E38 |
double | 数字类型(浮点型) | Double | 64 | 8 | 0d | 4.9E-324 ~ 1.7976931348623157E308 |
char | 字符类型 | Character | 16 | 2 | 'u0000' | 0 ~ 65535 |
boolean | 布尔类型 | Boolean | 1 | false | true、false |
从字节码中,我们发现装箱其实就是调用了 包装类的valueOf()
方法,拆箱其实就是调用了 xxxValue()
方法
成员变量与局部变量的区别有哪些?
- 语法形式 :
- 成员变量是属于类的,而局部变量是在代码块或方法中定义的变量或是方法的参数;
- 成员变量可以被
public
,private
,static
等修饰符所修饰,而局部变量不能被访问控制修饰符及static
所修饰; - 成员变量和局部变量都能被
final
所修饰。
- 存储方式 :
- 如果成员变量是使用
static
修饰的,那么这个成员变量是属于类的,如果没有使用static
修饰,这个成员变量是属于实例的。 - 而对象存在于堆内存,局部变量则存在于栈内存。
- 如果成员变量是使用
- 生存时间 :
- 成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动消失。
- 默认值 :
- 成员变量如果没有被赋初始值,则会自动以类型的默认值而赋值(一种情况例外:被
final
修饰的成员变量也必须显式地赋值),而局部变量则不会自动赋值
- 成员变量如果没有被赋初始值,则会自动以类型的默认值而赋值(一种情况例外:被
构造方法
如果一个类中没有声明构造,是可以正常使用的,因为会被提供一个默认的无参构造方法,我们平时直接 new对象的时候使用的其实就是无参构造。当我们在类中创建了其他构造方法的时候,就需要把无参构造也要加上。
特点:1.名字与类名相同 2.没有返回值 3生成类的对象时自动执行,不需要调用。4.构造方法不能被重写(override),但可以被重载(overload)
面向对象三大特征
- 封装: 把对象属性隐藏,不允许外部对象直接访问对象的内部信息,只提供方法来操作属性
- 继承(子对父):使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类
- 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
- 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
- 多态(父对子):
- 对象类型和引用类型之间具有继承(类)/实现(接口)的关系;
- 引用类型变量发出的方法调用的到底是哪个类中的方法,必须在程序运行期间才能确定;
- 多态不能调用“只在子类存在但在父类不存在”的方法;
- 如果子类重写了父类的方法,真正执行的是子类覆盖的方法,如果子类没有覆盖父类的方法,执行的是父类的方法
接口和抽象类有什么共同点和区别?
共同点 :
- 都不能被实例化。
- 都可以包含抽象方法。
- 都可以有默认实现的方法(Java 8 可以用
default
关键在接口中定义默认方法)。
区别 :
- 接口主要用于对类的行为进行约束,你实现了某个接口就具有了对应的行为。抽象类主要用于代码复用,强调的是所属关系(比如说我们抽象了一个发送短信的抽象类,)。
- 一个类只能继承一个类,但是可以实现多个接口。
- 接口中的成员变量只能是
public static final
类型的,不能被修改且必须有初始值,而抽象类的成员变量默认 default,可在子类中被重新定义,也可被重新赋值
深拷贝和浅拷贝区别了解吗?什么是引用拷贝?
- 浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。
- 深拷贝 :深拷贝会完全复制整个对象,包括这个对象所包含的内部对象
- 引用拷贝:就是两个不同的引用指向同一个对象。
Object 类的常见方法有哪些?
Object 类是一个特殊的类,是所有类的父类。它主要提供了以下 11 个方法:
//native方法,用于返回当前运行时对象的Class对象,使用了final关键字修饰,故不允许子类重写。
public final native Class<?> getClass()
//用于比较2个对象的内存地址是否相等,String类对该方法进行了重写用户比较字符串的值是否相等。
public boolean equals(Object obj)
//native方法,用于返回对象的哈希码,主要使用在哈希表中,比如JDK中的HashMap。
public native int hashCode()
//naitive方法,用于创建并返回当前对象的一份拷贝。一般情况下,对于任何对象 x,表达式 x.clone() != x 为true,x.clone().getClass() == x.getClass() 为true。
//Object本身没有实现Cloneable接口,所以不重写clone方法并且进行调用的话会发生CloneNotSupportedException异常。
protected native Object clone() throws CloneNotSupportedException
//返回类的名字@实例的哈希码的16进制的字符串。建议Object所有的子类都重写这个方法。
public String toString()
//native方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。
public final native void notify()
//native方法,并且不能重写。跟notify一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。
public final native void notifyAll()
//native方法,并且不能重写。暂停线程的执行。注意:sleep方法没有释放锁,而wait方法释放了锁 。timeout是等待时间。
public final native void wait(long timeout) throws InterruptedException
//多了nanos参数,这个参数表示额外时间(以毫微秒为单位,范围是 0-999999)。 所以超时的时间还需要加上nanos毫秒。
public final void wait(long timeout, int nanos) throws InterruptedException
//跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念
public final void wait() throws InterruptedException
//实例被垃圾回收器回收的时候触发的操作
protected void finalize() throws Throwable { }
泛型
Java 泛型了解么?什么是类型擦除?介绍一下常用的通配符?
java泛型是jdk5提供的特性,泛型提供编译时类型安全检查机制,该机制允许在编译时检测到非法的类型。泛型本质是参数化类型。
java的泛型是伪泛型,因为java在运行时所有泛型信息都会被擦除
public static void main(String[] args) throws Exception {
List<Integer> list = new ArrayList<>();
list.add(12);
// list.add("a"); //直接添加会报错,因为"a"不是数字
Class<? extends List> clazz = list.getClass();
Method add = clazz.getDeclaredMethod("add", Object.class);//这里的add是list的方法
//通过反射机制可以添加,也就说明在运行期间,泛型信息会被擦除
add.invoke(list,"k1");
System.out.println(list);//[12, k1]
}
泛型一般有3种使用方式:泛型类、泛型接口、泛型方法。
泛型类:
泛型类:
//此处T可以随便写成任意标示,常见的如T、E、K、V 等形式的参数常用语标示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{
private T key;
public Generic(T key){
this.key = key;
}
public T getKey(){
return key;
}
}
实例化泛型类:
Generic<Integer> c = new Generic<Integer>(123456);
泛型接口:
泛型接口:
public interface Generator<T>{
public T method();
}
实现泛型接口,不指定类型:
class GeneratorImpl<T> implements Generator<T>{
@Override
public T method(){
return null;
}
}
实现泛型接口,指定类型:
class GeneratorImpl implements Generator<String>{
@Override
public String method(){
return "";
}
}
泛型方法:
泛型方法:
public static <E> void printArray(E[] inputArray){
for(E element : inputArray){
system.out.printf("%s",element);
}
}
泛型方法的使用:
Integer[] intArray = { 1, 2, 3 };
printArray(intArray);
String[] stringArray = { "Hello", "World" };
printArray(stringArray);
常用的通配符为:T,E,K,V,?
- ?:表示不确定的Java类型
- T(Type):表示一个具体的java类型
- K V(key value): 分别代表Java键值中的key value
- E(element) :代表Element
常见用到泛型的地方:
- 用于定义通用返回结果的CommonResult<T> 通过参数 T 可根据具体的返回类型动态指定结果的数据类型
- 定义Excel处理类的ExcelUtil<T> 用于动态指定Excel 导出的数据类型
- 用于构建集合工具类。例如Collections中的sort,binarySearch方法
反射
何为反射?反射的优缺点以及应用场景 (包括注解的部分解释)
定义:反射是框架的灵魂,因为它赋予我们在运行时分析类以及执行类中方法的能力。通过反射可以获取任意一个类的所有属性和方法,也可以调用这些属性和方法。
优点:可以使代码更加灵活,为各种框架提供开箱即用的功能提供了便利
缺点:使我们在运行时有了分析操作类的能力,也增加了安全问题。比如可以无视泛型参数的安全检查。;另外,反射的性能也相对差些。不过对于框架而言,影响不大。
应用场景:在spring/springBoot、mybatis的步伐框架中都大量引用了反射机制。如注解
【注解(Annotation)是Java5引入的特性,可以看做为特殊的注释,主要用于修饰类、方法或者变量。 注解本身是一个继承了Annotation的特殊接口】
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
public interface Override extends Annotation{
}
注解只有被解析时才会生效,常见的解析方法有两种:
- 编译器直接扫描:编译器在编译代码时扫描对应的注解并处理,比如某些使用@Override 注解,编译器在编译的时候会检查当前的方法是否重写了父类对应的方法。
- 运行器通过反射处理:像框架中自带的注解(比如spring中的@Value @Deprecated),同时我们可以自定义注解。
Exception和Error
Exception和Error的区别
在java中所有异常都有一个共同的类:java.lang下的Throwable类。Throwable列有两个重要子类:Exception和Error
Exception:程序本身可以处理的异常,可以通过catch进行捕获。Exception又分为Checked Exception(受检查异常必须处理)和Unchecked Exception(不受检查异常,可以不处理)
Error:Error属于程序无法处理的错误,无法被捕获。例如:java虚拟机运行错误(Virtual MachineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等。这些异常发生时,JVM一般会选择线程终止。
Checked Exception 和 Unchecked Exception 有什么区别?
Checked Exception:在代码编译过程中,如果受检查异常没有被catch/throw 处理的话,就没办法通过编译。如在IO操作或者时间格式转化时,总是提示需要try catch 或者 throw Exception。 此类的异常常见的有:IO相关的异常,ClassNotFoundException、SQLException..。
Unchecked Exception:在编译过程中即使不处理不受检查异常也可以正常通过编译。RuntimeException(运行时异常)及其子类都统称非受检查异常,例如:NullPointerException(空指针异常)、NumberFormatException(字符串转化数字异常)、ArrayIndexOutOfBoundException(数组越界异常)、ClassCastException(类型转化异常)、ArithmeticException(算数错误异常)等
Throwable类常用的方法
- String getMessage():返回异常发生时的简要描述
- String toString():返回异常发生时的详细信息
- String getLocalizedMessage():返回异常对象的本地化信息。使用Throwable 的子类覆盖这个方法可以生成本地化信息。如果子类没有覆盖该方法,返回信息则与getMessage()一样
- void printStackTrace():在控制台上打印Throwable 对象封装的信息
try catch finally 的使用
- try:捕获异常。后面 catch 与 finally至少跟一个
- catch:处理异常
- finally:无论是否捕获异常,里面的语句都会在return之前被执行(注意:不要在finally中使用return。当try和finally中都有return时,try的return会被忽略掉)[当finally被执行前,虚拟机被终止运行,则不会被执行到了,同样,程序所在线程死亡、关闭CPU同样不会被执行到]
如何使用 try-with-resources
代替try-catch-finally
?(**)
- 适用范围(资源的定义): 任何实现
java.lang.AutoCloseable
或者java.io.Closeable
的对象 - 关闭资源和 finally 块的执行顺序: 在
try-with-resources
语句中,任何 catch 或 finally 块在声明的资源关闭后运行
理解try-with-resources语句及示例
try-with-resources语句是Java7提供的,是异常处理的一大利器。
一般使用时都是在使用try-catch-finally,但一旦遇到在finally中也需要进行try-catch的时候,就会显得臃肿且代码冗余,如下:
...
InputStream is = null;
try {
is = new FileInputStream("test");
is.read();
...
} catch (Exception e) {
...
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
...
}
}
java7提供了try-with-resources来自动关闭资源。资源的初始化是在try()中完成的,try后面引入了一对小括号用于其中完成资源的初始化。如下:
try (InputStream is = new FileInputStream("test")) {
is.read();
...
} catch(Exception e) {
...
} finally {
//no need to add code to close InputStream, its close method will be internally called
//(无需添加关闭InputStream输入流的代码,其close()方法会自行调用)
}
try-with-resource的一些注意事项
- 只有实现了AutoCloseable接口的类才可使用try-with-resource语句否则会出现编译错误。
- close方法是AutoCloseable接口的唯一方法,该方法在程序运行时会被自动调用。
- 可以在同一个try中声明多个资源类,如:try(A a = new A();B b = new B();){...}
- try中的多个资源在初始化过程中,一旦发生异常,那已完成初始化的资源将按照创建时间相反的顺序依次关闭。
- 如果try中用到了多个资源类,则close方法会按照其与其声明时相反的顺序依次调用。
- try-with-resources也可以与catch和finally连用,功能与以前一样。
- 当在try-with-resources语句中遇到了异常,close关闭会先于catch语句执行
- 实现AutoCloseable接口时,最佳做法是抛出具体异常,而不是抛出Exception本身,因为AutoCloseable接口的close方法本身抛出的异常就是Exception。
- 实现AutoCloseable接口时,AutoCloseable.close()方法不宜抛出InterrupedException被中断异常,因为程序运行时一旦该异常被抑制,将会引发指针处理方面的问题。
- Closeable接口继承了AutoCloseable接口,其close方法是幂等的。
- close方法不能是幂等的,但最佳做法仍然是将其实现为幂等的。也就是说,即便close方法被多次调用,其结果都是一致的
- try中声明的资源,会在try代码块开始执行之前完成资源的实例化
- try-with-resources中的资源会被隐式地声明为final
public class Lion implements AutoCloseable {
public Lion() {
System.out.println("LION is OPEN in the wild.");
};
public void hunt() throws Exception {
throw new Exception("DeerNotFound says Lion!");
}
public void close() throws Exception {
System.out.println("LION is CLOSED in the cage.");
throw new Exception("Unable to close the cage!");
}
}
public class Tiger implements AutoCloseable {
public Tiger() {
System.out.println("TIGER is OPEN in the wild.");
};
public void hunt() throws Exception {
throw new Exception("DeerNotFound says Tiger!");
}
public void close() throws Exception {
System.out.println("TIGER is CLOSED in the cage.");
}
}
public static void main(String[] args) {
try (Lion lion = new Lion(); Tiger tiger = new Tiger()) {
lion.hunt();
tiger.hunt();
} catch (Exception e) {
System.out.println(e);
} finally {
System.out.println("Finally.");
}
}
//得到允许结果为:
LION is OPEN in the wild.
TIGER is OPEN in the wild.
TIGER is CLOSED in the cage.
LION is CLOSED in the cage.
java.lang.Exception: DeerNotFound says Lion!
Finally.
如果try代码块中抛出了异常,则控制权转移到catch部分。在控制权跳转过程中,该资源会自动调用close()方法。狮子类在close的时候也抛出了个异常,但没有显示,是因为该异常在close抛出的时候被抑制了,而try的时候的异常时被真正抛出,如果想看被抑制的异常,需要用Throwable.getSuppressed()方法。如果有多个资源需要关闭,且在关闭其中一个资源时抛出了异常,则该异常将不会影响其他资源正常关闭。(个人理解:在try后的括号内进行初始化得到1,2,try内容中调用存在异常的hunt()方法,会让try进行关闭,于是就有了3,4的close()方法内容,此时try已经完事,catch捕获异常,首先出现异常的位置是5处,所以就没有了Tiger的hunt()方法抛的异常了)
/********************************************************************************************************/
本文仅做学习参考,还有很多未完善之处,需要补充内容请私信,若知道参考链接最好了,链接内容会一并放到下面
参考内容如下:
主页 | JavaGuide理解try-with-resources语句及示例_安冬的码畜日常的博客-CSDN博客_try-with-resources使用示例