文章目录
- 1. 你是怎样理解 OOP面向对象?
- 2. 重载与重写区别
- 3. 接口与抽象类的区别
- 4. 深拷贝与浅拷贝的理解
- 5. 什么是自动拆装箱? int和 Integer有什么区别
- 6. ==和 equals()区别
- 7. String类 能被继承吗为什么用 final修饰
- 8. final、finally、finalize区别
- 9. Object 类的常见方法有哪些?
- 10. 说一下集合体系
- 11. Java中有几种类型的流?
- 12. 请写出你最常见的 5个 RuntimeException
- 13. 谈谈你对反射的理解
- 14. 什么是java序列化,如何实现java序列化?
- 15. java new一个对象还有其他方式吗?
1. 你是怎样理解 OOP面向对象?
面向对象是利于语言对现实事物进行抽象。面向对象具有以下特征:
- 继承:继承是从已有类得到继承信息创建新类的过程
- 封装:封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义
的接口 - 多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应
2. 重载与重写区别
- 重载发生在本类,重写发生在父类与子类之间
- 重载的方法名必须相同,重写的方法名相同且返回值类型必须相同
- 重载的参数列表不同,重写的参数列表必须相同
- 重写的访问权限不能比父类中被重写的方法的访问权限更低
- 构造方法不能被重写
3. 接口与抽象类的区别
- 抽象类要被子类继承,接口要被类实现
- 抽象类可以有构造器、接口不能有构造器
- 抽象类:可以有成员变量;接口:只能声明常量
4. 深拷贝与浅拷贝的理解
深拷贝和浅拷贝就是指对象的拷贝,一个对象中存在两种类型的属性,一种是基本数据类型,一种是实例对象的引用。
- 浅拷贝是指,只会拷贝基本数据类型的值,以及实例对象的引用地址,并不会复
制一份引用地址所指向的对象,也就是浅拷贝出来的对象,内部的类属性指向的
是同一个对象 - 深拷贝是指,既会拷贝基本数据类型的值,也会针对实例对象的引用地址所指向
的对象进行复制,深拷贝出来的对象,内部的类执行指向的不是同一个对象
5. 什么是自动拆装箱? int和 Integer有什么区别
- 装箱:将基本类型转换成包装类对象
- 拆箱:将包装类对象转换成基本类型的值
java 为什么要引入自动装箱和拆箱的功能?
主要是用于 java集合中,List list=new ArrayList();
list 集合如果要放整数的话,只能放对象,不能放基本类型,因此需要将整数自动装箱成对象。
区别:
- Integer是 int的包装类,int则是 java的一种基本数据类型
- Integer变量必须实例化后才能使用,而 int变量不需要
- Integer实际是对象的引用,当 new一个 Integer时,实际上是生成一个指针指向此对象;而 int则是直接存储数据值
- Integer的默认值是 null,int的默认值是 0
6. ==和 equals()区别
- ==
- 如果比较的是基本数据类型,那么比较的是变量的值
- 如果比较的是引用数据类型,那么比较的是地址值(两个对象是否指向同一块内
存)
- equals
- 如果没重写 equals方法比较的是两个对象的地址值
- 如果重写了 equals方法后我们往往比较的是对象中的属性的内容
- equals 方法是从 Object类中继承的,默认的实现就是使用==
很多类(如 String, Integer 等)都重写了 equals() 方法,以提供逻辑上的比较。
7. String类 能被继承吗为什么用 final修饰
- 不能被继承,因为 String类有 final修饰符,而 final修饰的类是不能被继承的
- String 类是最常用的类之一,为了效率,禁止被继承和重写。
- 为了安全。String类中如果方法可以重写,可能被植入恶意代码,破坏程序。Java的安全性也体现在这里。
8. final、finally、finalize区别
-
final
final 是一个关键字,用于限定变量、方法、和类的行为:- 变量:当 final 修饰一个变量时,这意味着该变量一旦被初始化之后,其值就不能再被修改(即常量)。
- 方法:final 修饰的方法不能被子类重写。
- 类:final 修饰的类不能被继承。
-
finally
finally 是一个块,通常与try和catch块一起使用,在异常处理中起到关键作用。finally 块中的代码段总是会执行,无论try块中的代码是否抛出异常。这使得finally块非常适合用于清理资源,比如关闭文件流或数据库连接。
例如:
try {
// 尝试执行的代码,可能会抛出异常
} catch (Exception e) {
// 处理异常
} finally {
// 清理代码,无论是否发生异常都会执行
}
- finalize
finalize 用于对象被垃圾回收前的清理工作,但不建议使用。因为它是不可预测的,通常也不是必须的,而且可能导致性能问题。
@Override
protected void finalize() throws Throwable {
try {
// 清理资源等操作
} finally {
super.finalize();
}
}
9. Object 类的常见方法有哪些?
在 Java 中,Object
类是所有类的超类,它提供了一些常见的方法,这些方法可以在任何 Java 对象上调用。以下是 Object
类的常见方法及其简要说明:
-
equals(Object obj)
:- 用于比较两个对象是否“相等”。默认实现是比较对象的内存地址,子类通常会重写此方法以比较对象的内容。
-
hashCode()
:- 返回对象的哈希码,通常与
equals
方法一起重写。根据 Java 规范,如果两个对象根据equals
方法是相等的,那么它们的hashCode
值也必须相等。
- 返回对象的哈希码,通常与
-
toString()
:- 返回对象的字符串表示。默认实现返回类名和对象的内存地址,子类通常会重写此方法以提供更有意义的字符串表示。
-
getClass()
:- 返回对象的运行时类,返回的
Class
对象包含有关该对象所属类的信息。
- 返回对象的运行时类,返回的
-
clone()
:- 创建并返回对象的副本。类必须实现
Cloneable
接口才能合法地调用此方法,否则会抛出CloneNotSupportedException
异常。
- 创建并返回对象的副本。类必须实现
-
finalize()
:- 在垃圾回收器决定回收对象之前调用,用于进行清理操作。已经被废弃,不推荐使用。
-
wait()
:- 使当前线程等待,直到其他线程调用此对象的
notify()
方法或notifyAll()
方法。常用于线程间的同步。
- 使当前线程等待,直到其他线程调用此对象的
-
notify()
:- 唤醒在此对象监视器上等待的单个线程。常用于线程间的同步。
-
notifyAll()
:- 唤醒在此对象监视器上等待的所有线程。常用于线程间的同步。
这些方法是 Java 中所有对象的基本方法,了解并合理使用它们可以帮助你更好地掌握 Java 编程。
10. 说一下集合体系
Java 集合,也叫作容器,主要是由两大接口派生而来:一个是 Collection
接口,主要用于存放单一元素;另一个是 Map
接口,主要用于存放键值对。
- 核心接口
- Collection 接口:
- 是集合层次结构的根接口,包含操作集合的基本方法。主要的子接口包括 List、Set 。
- Map 接口:
- 不属于 Collection 接口的子接口,但经常与集合框架一起使用。它存储键值对(key-value pairs),不允许键重复。主要实现类包括 HashMap、LinkedHashMap 和 TreeMap。
- 主要实现类
set:
- HashSet:
- 基于哈希表的实现,不保证顺序,适合快速查找和去重的场景。
- LinkedHashSet:
- 保持插入顺序的 HashSet,适合需要顺序且去重的场景。
- TreeSet:
- 基于红黑树的实现,保证元素的自然顺序或自定义顺序,适合需要排序的场景。
List:
- ArrayList:
- 基于动态数组的实现,提供随机访问能力,适合频繁读取的场景。
- LinkedList:
- 基于双向链表的实现,适合频繁插入和删除的场景。
map:
- HashMap:
- 基于哈希表的实现,允许使用 null 键和 null 值,不保证顺序,适合快速查找和键值映射的场景。
- LinkedHashMap:
- 保持插入顺序的 HashMap,适合需要顺序且快速查找的场景。
- TreeMap:
- 基于红黑树的实现,保证键的自然顺序或自定义顺序,适合需要排序的键值映射场景。
10.1 说说 List, Set, Queue, Map 四者的区别?
List
: 存储的元素是有序的、可重复的。Set
: 存储的元素不可重复的。Queue:
按特定的排队规则来确定先后顺序,存储的元素是有序的、可重复的。Map:
使用键值对(key-value)存储,类似于数学上的函数 y=f(x),“x” 代表 key,“y” 代表 value,key 是无序的、不可重复的,value 是无序的、可重复的,每个键最多映射到一个值。
10.2 如何选用集合?
我们主要根据集合的特点来选择合适的集合。比如:
- 我们需要根据键值获取到元素值时就选用
Map
接口下的集合,需要排序时选择TreeMap
,不需要排序时就选择HashMap
,需要保证线程安全就选用ConcurrentHashMap
。 - 我们只需要存放元素值时,就选择实现
Collection
接口的集合,需要保证元素唯一时选择实现Set
接口的集合比如TreeSet
或HashSet
,不需要就选择实现List
接口的比如ArrayList
或LinkedList
,然后再根据实现这些接口的集合的特点来选用。
10.3 为什么要使用集合?
当我们需要存储一组类型相同的数据时,数组是最常用且最基本的容器之一。与数组相比,Java 集合提供了更灵活、更有效的方法来存储多个数据对象。Java 集合框架中的各种集合类和接口可以存储不同类型和数量的对象,同时还具有多样化的操作方式。相较于数组,Java 集合的优势在于它们的大小可变、支持泛型、具有内建算法等。总的来说,Java 集合提高了数据的存储和处理灵活性,可以更好地适应现代软件开发中多样化的数据需求,并支持高质量的代码编写。
11. Java中有几种类型的流?
在 Java 中,流(Stream)通常用于输入和输出操作。Java 的 I/O 流主要分为两大类:字节流和字符流。每种类型的流都有输入流和输出流。以下是详细的分类:
- 字节流(Byte Streams)
字节流用于处理原始的二进制数据。它们以字节(8位)为单位来进行数据的读写操作。
-
输入字节流(Input Byte Streams)
- InputStream(抽象类):所有字节输入流的超类。
- FileInputStream:从文件中读取字节。
- BufferedInputStream:为另一个输入流添加缓冲功能。
- InputStream(抽象类):所有字节输入流的超类。
-
输出字节流(Output Byte Streams)
- OutputStream(抽象类):所有字节输出流的超类。
- FileOutputStream:将字节写入文件。
- BufferedOutputStream:为另一个输出流添加缓冲功能。
- OutputStream(抽象类):所有字节输出流的超类。
- 字符流(Character Streams)
字符流用于处理字符数据,它们以字符(16位 Unicode)为单位来进行数据的读写操作。字符流适用于处理文本数据。
- 输入字符流(Input Character Streams)
- Reader(抽象类):所有字符输入流的超类。
- FileReader:从文件中读取字符。
- BufferedReader:为另一个输入流添加缓冲功能,并提供 readLine 方法读取一行文本。
- InputStreamReader:将字节流转换为字符流,通常与其他字节流组合使用。
- Reader(抽象类):所有字符输入流的超类。
- 输出字符流(Output Character Streams)
- Writer(抽象类):所有字符输出流的超类。
- FileWriter:将字符写入文件。
- BufferedWriter:为另一个输出流添加缓冲功能,并提供 newLine 方法写入行分隔符。
- OutputStreamWriter:将字符流转换为字节流,通常与其他字节流组合使用。
- Writer(抽象类):所有字符输出流的超类。
12. 请写出你最常见的 5个 RuntimeException
13. 谈谈你对反射的理解
-
反射机制
所谓的反射机制就是 java语言在运行时拥有一项自观的能力。通过这种能力可以彻底了解自身的情况为下一步的动作做准备。
Java 的反射机制的实现要借助于 4个类:class,Constructor,Field,Method;其中 class代表的是类对象,Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象。通过这四个对象我们可以粗略的看到一个类的各个组成部分。
-
Java反射的作用
在 Java运行时环境中,对于任意一个类,可以知道这个类有哪些属性和方法。对于任意一个对象,可以调用它的任意一个方法。这种动态获取类的信息以及动态调用对象的方法的功能来自于 Java语言的反射(Reflection)机制。
-
Java反射机制提供功能
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时判断任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法
14. 什么是java序列化,如何实现java序列化?
Java 序列化(Serialization)是将对象的状态转换为字节流,以便将对象保存到存储介质(如文件、数据库)中或通过网络传输到远程主机的过程。反序列化(Deserialization)是将字节流转换回对象的过程。序列化允许对象的持久化存储和跨网络传输。
实现 Java 序列化的步骤
要实现 Java 序列化,需要完成以下步骤:
-
实现
Serializable
接口:- 要使一个类的对象可序列化,该类必须实现
java.io.Serializable
接口。该接口是一个标记接口,没有任何方法。
- 要使一个类的对象可序列化,该类必须实现
-
使用
ObjectOutputStream
和ObjectInputStream
:ObjectOutputStream
用于将对象序列化为字节流。ObjectInputStream
用于将字节流反序列化为对象。
15. java new一个对象还有其他方式吗?
在Java中,除了使用new
关键字来创建对象之外,还有其他几种方式可以实例化对象。以下是一些常见的方法:
-
通过反射(Reflection):
使用Java的反射机制可以动态地创建对象。try { Class<?> clazz = Class.forName("com.example.MyClass"); Object obj = clazz.getDeclaredConstructor().newInstance(); } catch (Exception e) { e.printStackTrace(); }
-
使用克隆(Clone):
如果一个类实现了Cloneable
接口,并且重写了clone()
方法,可以通过克隆现有对象来创建新对象。public class MyClass implements Cloneable { @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } MyClass obj1 = new MyClass(); try { MyClass obj2 = (MyClass) obj1.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); }
-
使用序列化与反序列化:
通过将对象序列化为字节流然后反序列化,可以创建一个新的对象。import java.io.*; public class MyClass implements Serializable { private static final long serialVersionUID = 1L; } MyClass obj1 = new MyClass(); MyClass obj2 = null; try { // 序列化 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj1); oos.close(); // 反序列化 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); obj2 = (MyClass) ois.readObject(); ois.close(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); }
-
通过工厂方法(Factory Method):
工厂方法是设计模式中的一种,通过调用工厂类的静态方法来创建对象。public class MyClass { private MyClass() { // 私有构造函数 } public static MyClass createInstance() { return new MyClass(); } } MyClass obj = MyClass.createInstance();
-
使用DI框架(例如Spring):
依赖注入框架可以管理对象的创建和生命周期。import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); MyClass obj = (MyClass) context.getBean("myClassBean");
这些方法各有其适用的场景和优缺点,选择合适的方法取决于具体的应用需求和设计模式。