Java基础
- Java基础
- 介绍一下Java
- 面向对象的三大特点
- JDK与JRE有什么区别?
- java 有哪些数据类型?
- 包装类型的常量池技术了解么?
- 自动装箱与拆箱了解吗?原理是什么?
- 简述Java访问修饰符
- 构造方法、成员变量初始化以及静态成员变量三者的初始化顺序?
- 接口和抽象类的相同点和区别?
- 重载和重写什么区别?
- 常见的异常有哪些?
- 异常要怎么解决?
- 简述throw与throws的区别
- == 和 equals() 的区别
- hashCode() 与 equals()
- final、finally和finalize的区别是什么?
- 出现在Java程序中的finally代码块是否一定会执行?
- 什么是泛型?
- 泛型擦除是什么?
- 介绍一下常用的通配符?
- 创建对象有哪些方式
- 什么是反射
- 反射的应用场景(动态代理)
- 序列化/反序列化是什么?
- 深拷贝和浅拷贝区别了解吗?
Java基础
介绍一下Java
java 是一门**「开源的跨平台的面向对象的」**计算机语言。
跨平台是因为 java 的 class 文件是运行在虚拟机上的,并且「虚拟机是不同平台有不同版本」,所以说 java 是跨平台的。
面向对象的三大特点
1.「封装」
- 两层含义:一层含义是把对象的属性和行为看成一个密不可分的整体,将这两者封装在一个不可分割的「独立单元」(即对象)中
- 另一层含义指信息隐藏,把不需要让外界知道的信息隐藏起来,有些对象的属性及行为允许外界用户知道或使用,但不允许更改,而另一些属性或行为,则不允许外界知晓,或只允许使用对象的功能,而尽可能「隐藏对象的功能实现细节」。
「优点」:
- 良好的封装能够「减少耦合」,符合程序设计追求高内聚,低耦合
- 「类内部的结构可以自由修改」
- 可以对成员变量进行更「精确的控制」
- 「隐藏信息」实现细节
2.「继承」
- 继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
「优点」:
- 提高类代码的「复用性」
- 提高了代码的「维护性」
3.「多态」
多态是同一个行为具有多个不同表现形式或形态的能力。Java语言中含有方法重载与对象多态两种形式的多态:
- 「方法重载」:在一个类中,允许多个方法使用同一个名字,但方法的参数不同,完成的功能也不同。
- 「对象多态」:子类对象可以与父类对象进行转换,而且根据其使用的子类不同完成的功能也不同(重写父类的方法)。
「优点」
- 「消除类型之间的耦合关系」
- 「可替换性」
- 「可扩充性」
- 「接口」
- 「灵活性」
- 「简化性」
JDK与JRE有什么区别?
- JDK:Java开发工具包(Java Development Kit),提供了Java的开发环境和运行环境。
- JRE:Java运行环境(Java Runtime Environment),提供了Java运行所需的环境。 JDK包含了JRE。
如果只运行Java程序,安装JRE即可。要编写Java程序需安装JDK。
java 有哪些数据类型?
包装类型的常量池技术了解么?
Java 基本类型的包装类的大部分都实现了常量池技术。
Byte
,Short
,Integer
,Long
这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character
创建了数值在 [0,127] 范围的缓存数据,Boolean
直接返回 True
or False
。
两种浮点数类型的包装类 Float
,Double
并没有实现常量池技术。
自动装箱与拆箱了解吗?原理是什么?
什么是自动拆装箱?
- 装箱:将基本类型用它们对应的引用类型包装起来;
- 拆箱:将包装类型转换为基本数据类型;
装箱其实就是调用了 包装类的valueOf()
方法,拆箱其实就是调用了 xxxValue()
方法。
因此,
Integer i = 10
等价于Integer i = Integer.valueOf(10)
;int n = i
等价于int n = i.intValue()
;
简述Java访问修饰符
- default: 默认访问修饰符,在同一包内可见
- private: 在同一类内可见,不能修饰类
- protected : 对同一包内的类和所有子类可见,不能修饰类
- public: 对所有类可见
构造方法、成员变量初始化以及静态成员变量三者的初始化顺序?
先后顺序:静态成员变量、成员变量、构造方法。
详细的先后顺序:父类静态变量、父类静态代码块、子类静态变量、子类静态代码块、父类非静态变量、父类非静态代码块、父类构造函数、子类非静态变量、子类非静态代码块、子类构造函数。
接口和抽象类的相同点和区别?
相同点:
- 都不能被实例化。
- 接口的实现类或抽象类的子类需实现接口或抽象类中相应的方法才能被实例化。
不同点:
-
接口是抽象类的变体,「接口中所有的方法都是抽象的」。而抽象类是声明方法的存在而不去实现它的类。
-
接口可以多继承,抽象类不行。
-
接口定义方法,不能实现,默认是 「public abstract」,而抽象类可以实现部分方法。
-
接口中基本数据类型为 「public static final」 并且需要给出初始值,而抽类象不是的。
重载和重写什么区别?
重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理。
重写就是子类对父类方法的重新改造,外部样子不能改变,内部逻辑可以改变。
方法的重写要遵循“两同两小一大”:
- “两同”即方法名相同、形参列表相同;
- “两小”指的是子类方法返回值类型应比父类方法返回值类型更小或相等,子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等;
- 注意:如果方法的返回类型是 void 和基本数据类型,则返回值重写时不可修改
- “一大”指的是子类方法的访问权限应比父类方法的访问权限更大或相等。
常见的异常有哪些?
NullPointerException
空指针异常ArrayIndexOutOfBoundsException
索引越界异常InputFormatException
输入类型不匹配SQLException
SQL异常IllegalArgumentException
非法参数NumberFormatException
类型转换异常
异常要怎么解决?
Java标准库内建了一些通用的异常,这些类以Throwable为顶层父类。
![types-of-exceptions-in-java](https://javaguide.cn/assets/img/types-of-exceptions-in-java.3508827e.png)
![img](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/2020-12/Java%E5%BC%82%E5%B8%B8%E7%B1%BB%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84%E5%9B%BE2.png)
Throwable
又派生出**「Error类和Exception类」**。
错误:Error类以及他的子类的实例,代表了JVM本身的错误。错误不能被程序员通过代码处理,Error很少出现。常见的有StackOverFlowError
,OutOfMemoryError
异常:Exception以及他的子类,代表程序运行时发送的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。Exception可分为运行时异常和非运行时异常。
- 对于运行时异常:可以利用
try catch
的方式进行处理,也可以不处理。 - 对于非运行时异常:必须处理,不处理的话程序无法通过编译。
处理方法:
- 「try()catch(){}」
try{
// 程序代码
}catch(ExceptionName e1){
//Catch 块
}
-
「throw」
- throw 关键字作用是抛出一个异常,抛出的时候是抛出的是一个异常类的实例化对象,在异常处理中,try 语句要捕获的是一个异常对象,那么此异常对象也可以自己抛出
-
「throws」
- 定义一个方法的时候可以使用 throws 关键字声明。使用 throws 关键字声明的方法表示此方法不处理异常,而交给方法调用处进行处理。
简述throw与throws的区别
- throw一般是用在方法体的内部,由开发者定义当程序语句出现问题后主动抛出一个异常。
- throws一般用于方法声明上,代表该方法可能会抛出的异常列表。
== 和 equals() 的区别
==
对于基本类型和引用类型的作用效果是不同的:
- 对于基本数据类型来说,
==
比较的是值。 - 对于引用数据类型来说,
==
比较的是对象的内存地址。
equals()
不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。equals()
方法存在于Object
类中,而Object
类是所有类的直接或间接父类。
equals()
方法存在两种使用情况:
- 类没有覆盖
equals()
方法 :通过equals()
比较该类的两个对象时,等价于通过“==”比较这两个对象,使用的默认是Object
类equals()
方法。 - 类覆盖了
equals()
方法 :一般我们都覆盖equals()
方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回 true(即,认为这两个对象相等)。
hashCode() 与 equals()
“你重写过 hashCode()
和 equals()
么? 为什么重写 equals()
时必须重写 hashCode()
方法?”
-
因为两个相等的对象的
hashCode
值必须是相等。也就是说如果equals
方法判断两个对象是相等的,那这两个对象的hashCode
值也要相等。 -
如果重写
equals()
时没有重写hashCode()
方法的话就可能会导致equals
方法判断是相等的两个对象,hashCode
值却不相等。
总结 :
equals
方法判断两个对象是相等的,那这两个对象的hashCode
值也要相等。- 两个对象有相同的
hashCode
值,他们也不一定是相等的(哈希碰撞)。
final、finally和finalize的区别是什么?
- final用于声明属性、方法和类,分别表示属性不可变、方法不可覆盖、类不可继承。
- finally作为异常处理的一部分,只能在
try/catch
语句中使用,finally附带一个语句块用来表示这个语句最终一定被执行,经常被用在需要释放资源的情况下。 - finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的finalize()方法。当垃圾回收器准备好释放对象占用空间时,首先会调用finalize()方法,并在下一次垃圾回收动作发生时真正回收对象占用的内存。
出现在Java程序中的finally代码块是否一定会执行?
当遇到下面情况不会执行。
- 当程序在进入try语句块之前就出现异常时会直接结束。
- 当程序在try块中强制退出时,如使用System.exit(0),也不会执行finally块中的代码。
其它情况下,在try/catch/finally语句执行的时候,try块先执行,当有异常发生,catch和finally进行处理后程序就结束了,当没有异常发生,在执行完finally中的代码后,后面代码会继续执行。值得注意的是,当try/catch语句块中有return时,finally语句块中的代码会在return之前执行。如果try/catch/finally块中都有return语句,finally块中的return语句会覆盖try/catch模块中的return语句。
什么是泛型?
泛型:「把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型」
泛型擦除是什么?
因为泛型其实只是在编译器中实现的而虚拟机并不认识泛型类型,所以要在虚拟机中将泛型类型进行擦除。也就是说,「在编译阶段使用泛型,运行阶段取消泛型,即擦除」。擦除是将泛型类型以其父类代替,如String 变成了Object等。其实在使用的时候还是进行带强制类型的转化,只不过这是比较安全的转换,因为在编译阶段已经确保了数据的一致性。
介绍一下常用的通配符?
T,E,K,V,?
-
? 表示不确定的 Java 类型
-
T (type) 表示具体的一个 Java 类型
-
K V (key value) 分别代表 Java 键值中的 Key Value
-
E (element) 代表 Element
创建对象有哪些方式
有「五种创建对象的方式」
- 1.
new关键字
Person p1 = new Person();
- 2.
Class.newInstance
Person p1 = Person.class.newInstance();
- 3.
Constructor.newInstance
Constructor<Person> constructor = Person.class.getConstructor();
Person p1 = constructor.newInstance();
- 4.clone
Person p1 = new Person();
Person p2 = p1.clone();
- 5.反序列化
Person p1 = new Person();
byte[] bytes = SerializationUtils.serialize(p1);
Person p2 = (Person)SerializationUtils.deserialize(bytes);
什么是反射
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
反射的应用场景(动态代理)
要想实现动态代理,需要解决的问题?
- 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
- 问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。
使用 Proxy
类里面的方法创建代理对象
调用 newProxyInstance 方法,方法有三个参数:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
-
第一参数,类加载器;通过对象获取类的加载器:
obj.getClass().getClassLoader()
-
第二参数,增强方法所在的类实现的接口,支持多个接口;通过对象获取类的实现的接口:
obj.getClass().getInterfaces()
-
第三参数,实现这个接口 InvocationHandler,创建代理对象,添加增强方法的部分;
定义实现 **InvocationHandler
**接口
重写invoke
方法
class ProxyFactory{
//调用此方法,返回一个代理类的对象。解决问题一
public static Object getProxyInstance(Object obj){ //obj:被代理类的对象
MyInvocationHandler hander = new MyInvocationHandler();
hander.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),hander);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj;//需要使用被代理类的对象进行赋值
public void bind(Object obj){
this.obj = obj;
}
//当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()。解决问题二
//将被代理类要执行的方法a的功能就声明在invoke()中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
//obj:被代理类的对象
Object returnValue = method.invoke(obj,args);
//上述方法的返回值就作为当前类中的invoke()的返回值。
return returnValue;
}
}
序列化/反序列化是什么?
-
序列化: 将对象转换成二进制字节流的过程
-
反序列化:将在序列化过程中所生成的二进制字节流转换成对象的过程
序列化可以将对象的状态写在流里进行网络传输,或者保存到文件、数据库等系统里,并在需要的时候把该流读取出来重新构造成一个相同的对象。
深拷贝和浅拷贝区别了解吗?
-
浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。
-
深拷贝 :深拷贝会完全复制整个对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
-
引用拷贝就是两个不同的引用指向同一个对象。