java基础
- java面向对象有哪些特征
- 多态的三要素
- 重载和重写的区别
- ArrayList和LinkedList有什么区别?
- Java 语言有哪些特点
- 什么是字节码?采用字节码的好处是什么?
- char 变量能不能存贮一个中文汉字?为什么?
- jdk与jre的区别是什么?
- 接口和抽象类的区别
- Comparable 和 Comparator 有哪些区别?
- == 和 equals 的区别是什么?
- final 在 java 中有什么作用??
- String 属于基础的数据类型吗??
- int 和 Integer 有什么区别
- int和Integer有什么区别,为什么要使用包装类?
- java 中 IO 流分为几种?
- 异常处理
- java 泛型
- 说说反射的用途及实现原理,Java 获取反射的三种方法
- 讲讲类的实例化顺序,比如父类静态数据,构造函数,子类静态数据,构造函数。
- Java 创建对象有几种方式
- 谈谈 Java 的异常层次结构
- 静态内部类与非静态内部类有什么区别
- String s 与 new String 与有什么区别
- 反射中,Class.forName 和 ClassLoader 的区别
- 深拷贝和浅拷贝
- 什么是拆装箱?
java面向对象有哪些特征
继承 封装 多态
封装:封装是将数据和相关操作封装在一个单元内,对外界来说他内部的细节是隐藏的,暴露给外界的是他的访问方法。
继承:允许一个类(子类/派生类)继承另一个类(父类/基类)的属性和方法。通过继承,子类可以重用父类的代码,并在其基础上进行扩展或修改。继承有助于实现代码的层次结构和代码重用,提高了代码的可扩展性。
多态:多态性就是同一个方法名可以被不同的对象用不同的方式调用。从而提高了代码的灵活性和可扩展性。多态可以通过方法的重写(覆盖)和方法的重载来实现。
多态的三要素
多态三要素:继承、重写和向上转型
1. 继承 2. 方法重写 3. 向上转型:父类引用接收子类对象(默认,隐式转型)
多态表现:父类引用指向子类对象(向上转型),父类引用调用方法执行的是子类重写的方法
重载和重写的区别
作用范围:重写的作用范围是父类和子类之间;重载是发生在一个类里面
参数列表:重载必须不同;重写不能修改
返回类型:重载可修改;重写方法返回相同类型或子类
访问权限:重载可修改;重写一定不能做更严格的限制
ArrayList和LinkedList有什么区别?
可以从它们的底层数据结构、效率、开销进行阐述哈
ArrayList是数组的数据结构,LinkedList是链表的数据结构。
随机访问的时候,ArrayList的效率比较高,因为LinkedList要移动指针,而ArrayList是基于索引(index)的数据结构,可以直接映射到。
插入、删除数据时,LinkedList的效率比较高,因为ArrayList要移动数据。
LinkedList比ArrayList开销更大,因为LinkedList的节点除了存储数据,还需要存储引用。
静态代理和动态代理的区别?
静态代理中代理类在编译期就已经确定,而动态代理则是JVM运行时动态生成,静态代理的效 率相对动态代理来说相对高一些,但是静态代理代码冗余大,一单需要修改接口,代理类和委 托类都需要修改
Java 语言有哪些特点
- 面向对象(封装,继承,多态);
- 平台无关性( Java 虚拟机实现平台无关性);
- 支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java语言却提供了多线程支持);
- 可靠性(具备异常处理和自动内存管理机制)
什么是字节码?采用字节码的好处是什么?
在 Java 中,JVM 可以理解的代码就叫做字节码(即扩展名为 .class 的文件),它不面向任何特定的处理器,只面向虚拟机。Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点
char 变量能不能存贮一个中文汉字?为什么?
char 变量可以存贮一个汉字,因为 Java 中使用的默认编码是 Unicode ,一个 char 类型占 2 个字节(16 bit),一个汉字是2个字节,所以放一个中文是没问题的。
jdk与jre的区别是什么?
jdk是java开发包,提供了java的开发环境和运行环境。而jre仅是java的运行环境,jdk包含jre以及其他编译工具和分析调试工具
接口和抽象类的区别
接口和抽象类是面向对象编程中的两种不同的概念,它们都用于定义类的结构和行为,但有一些关键区别。以下是一个简单的表格,用于概述它们之间的区别:
特点 | 接口 | 抽象类 |
---|---|---|
定义方式 | 使用 interface 关键字定义 | 使用 abstract 关键字定义 |
多继承 | 支持多重接口继承 | 不支持多重类继承 |
构造函数 | 不允许定义构造函数 | 允许定义构造函数 |
方法实现 | 所有方法都是抽象的,需要被实现 | 可以包含抽象方法和具体方法 |
变量 | 不允许定义实例变量 | 可以定义实例变量 |
继承关系 | 类可以同时实现多个接口 | 子类只能继承一个抽象类 |
总的来说,接口更适合用于定义类之间的契约和多态性,而抽象类更适合用于共享通用行为和结构,并且可以包含一些已实现的方法。选择接口还是抽象类取决于您的设计需求和类之间的关系。有时,它们也可以一起使用,以实现更复杂的继承和多态结构。
Comparable 和 Comparator 有哪些区别?
当比较 Java 中的 Comparable 和 Comparator 时,有一些关键区别需要考虑:
-
位置:
- Comparable 位于
java.lang
包下。 - Comparator 位于
java.util
包下。
- Comparable 位于
-
实现方式:
- Comparable 在排序类的内部实现,定义对象自身的排序规则。
- Comparator 在排序类的外部实现,允许外部定义多个不同的排序规则。
-
方法:
- Comparable 需要重写
compareTo()
方法,用于定义对象之间的自然排序规则。 - Comparator 需要重写
compare()
方法,用于定义不同的排序规则。
- Comparable 需要重写
这些区别可以帮助您选择何时使用 Comparable 和 Comparator,具体取决于您的需求和设计。
== 和 equals 的区别是什么?
双等于号对于基本类型,两者比较的是值,而对于引用类型,对比的是,其引用是否相同
equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较
final 在 java 中有什么作用??
final 修饰类,类不可以被继承。
final 修饰的方法不可以被重写
final 修饰的量是常量。必须初始化且不能被修改
String 属于基础的数据类型吗??
String 不属于基础类型,基础类型有 8 种:byte、boolean、char、short、int、float、long、double,而
String 属于对象
String 类能被继承吗,为什么。
首先,String 是一个 final 修饰的类,final 修饰的类不可以被继承。
- 效率性,String 类作为最常用的类之一,禁止被继承和重写,可以提高效率。
- 安全性,String 类中有很多调用底层的本地方法,调用了操作系统的 API,如果 方法可以重写,可能被植入恶意代码,破坏程序。
为什么 string 要设计成不可变??
(1) 节省空间:在 java 中,将字符串常量存储在字符串常量池中,这样这些字符串方便被共享,可以节省空间,同时防止其他线程修改该字符串,string 设计为不可变
(2) 提高效率,线程安全:正是由于 string 会被不同用户共享,在多线程编程时,在多线程共享string 时,因为 string 不可变所以 string 是线程安全且省略了同步的过程会显著提高效率。
String、StringBuffer、StringBuilder 的区别?
可变性: String 为字符串常量是不可变对象,StringBuffer 与 StringBuilder 为字符串变量是可变对象;
性能: String 它的底层是一个用 final 修饰的字符数组,每次修改相当于生成一个新对象,因此性能最低;StringBuffer 和 StringBuilder 底层使用的是没有用 final 修饰的字符数组 char[],所以在做字符串拼接的时候就在原来的内存上进行拼接,不会浪费内存空间。但是 StringBuffer 不同与StringBuilder 使用 synchronized 来保证线程安全,但两者性能均优于 String。;
int 和 Integer 有什么区别
int 是基本数据类型,interger 是 int 的封装类
int 默认值为 0 ,而 interger 默认值为 null, Interger 使用需要判空处理
int和Integer有什么区别,为什么要使用包装类?
答:为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型,比如int的包装类就是Integer,Java 为每个原始类型提供了包装类型:
java 中 IO 流分为几种?
主要分为输入输出流。字节流和字符流。字节流按八位传输。以字节为单位。进行输入输出。字符流按
16 位进行传输。以字符为单位进行输入输出。分别来看字符流(Reader、Writer)和字节流(InputStream、OutputStream)的使用。
① Writer 使用
Writer 可用来写入文件,请参考以下代码:
// 给指定目录下的文件追加信息
Writer writer = new FileWriter("d:\\io.txt",true);
writer.append("老王");
writer.close();
这几行简单的代码就可以实现把信息 老王 追加到 d:\\io.txt 的文件下,参数二表示的是覆盖文字还是追加文字。
② Reader 使用
Reader 可用来读取文件,请参考以下代码:
Reader reader = new FileReader("d:\\io.txt");
BufferedReader bufferedReader = new BufferedReader(reader);
String str = null;
// 逐行读取信息
while (null != (str = bufferedReader.readLine())) {
System.out.println(str);
}
bufferedReader.close();
reader.close();
③ InputStream 使用
InputStream 可用来读取文件,请参考以下代码:
InputStream inputStream = new FileInputStream(new File("d:\\io.txt"));
byte[] bytes = new byte[inputStream.available()];
// 读取到 byte 数组
inputStream.read(bytes);
// 内容转换为字符串
String content = new String(bytes, "UTF-8");
inputStream.close();
④ OutputStream 使用
OutputStream 可用来写入文件,请参考以下代码:
OutputStream outputStream = new FileOutputStream(new File("d:\\io.txt"),true);
outputStream.write("老王".getBytes());
outputStream.close();
同步与异步,阻塞与非阻塞
- 同步和异步指的是:当前线程是否需要等待方法调用执行完毕。
比如你调用一个搬运一百块石头的方法:
同步指的是调用这个方法,你的线程需要等待这一百块石头搬完,然后得到搬完了的结果,接着
再继续执行剩下的代码逻辑。
异步指的是调用这个方法,立马就直接返回,不必等候这一百块石头还未搬完,可以立马执行后
面的代码逻辑,然后利用回调(这里也能体现了回调函数的应用场景)或者事件通知的方式得到
石头已经搬完的结果 - 阻塞和非阻塞指的是:当前接口数据还未准备就绪时,线程是否被阻塞挂起。
何为阻塞挂起?就是当前线程还处于 CPU 时间片当中,调用了阻塞的方法,由于数据未准备就
绪,则时间片还未到就让出 CPU。
所以阻塞和同步看起来都是等,但是本质上它们不一样,同步的时候可没有让出 CPU。
而非阻塞就是当前接口数据还未准备就绪时,线程不会被阻塞挂起,可以不断轮询请求接口,看
看数据是否已经准备就绪。 - 至此我们可以得到一个结论:
同步, 异步指:当数据还未处理完成时,代码的逻辑处理方式不同。
阻塞, 非阻塞指:当数据还未处理完成时 (未就绪),线程的状态。所以同步, 异步其实是处于框架
这种高层次维度来看待的,而阻塞与非阻塞往往针对底层的系统调用方面来抉择,也就是说两者
是从不同维度来考虑的。
同步与异步,阻塞与非阻塞 IO
-
前提:程序和硬件之间隔了个操作系统,而为了安全考虑,Linux 系统分了:用户态和内核态
在这个前提下,我们再明确 I/O 操作有两个步骤:1.发起 I/O 请求 2.实际 I/O 读写(即数据从内核缓存拷贝到用户空间). -
阻塞 I/O 和非阻塞 I/O。按照上文,其实指的就是用户线程是否被阻塞,这里指代的步骤 1
(发起 I/O 请求)。 -
阻塞 I/O,指用户线程发起 I/O 请求的时候,如果数据还未准备就绪(例如暂无网络数据接 收),就会阻塞当前线程,让出 CPU。
-
非阻塞 I/O,指用户线程发起 I/O 请求的时候,如果数据还未准备就绪(例如暂无网络数据
接收),也不会阻塞当前线程,可以继续执行后续的任务。可以发现,这里的阻塞和非阻塞其实 是指用户线程是否会被阻塞。 -
同步 I/O 和异步 I/O。按照上文,我们可以得知这就是根据 I/O 响应方式不同而划分的。
-
同步 I/O,指用户线程发起 I/O 请求的时候,数据是有的,那么将进行步骤 2(实际 I/O 读
写,即数据从内核缓存拷贝到用户空间),这个过程用户线程是要等待着拷贝完成。 -
异步 I/O,指用户线程发起 I/O 请求的时候,数据是有的,那么将进行步骤 2(实际 I/O 读
写,即数据从内核缓存拷贝到用户空间),拷贝的过程中不需要用户线程等待,用户线程可以去
执行其它逻辑,等内核将数据从内核空间拷贝到用户空间后,用户线程会得到一个“通知”。再 仔细思考下,在 I/O场景下同步和异步说的其实是内核的实现,因为拷贝的执行者是内核,一种是同步将数据拷贝到用户空间,用户线程是需要等着的。一个是通过异步的方式,用户线程不 用等,在拷贝完之后,内核会调用指定的回调函数。
我再简单的总结一下,关于 I/O 的阻塞、非阻塞、同步、异步:
阻塞和非阻塞指的是发起 I/O 请求后,用户线程状态的不同,阻塞 I/O 在数据未准备就绪的
时候会阻塞当前用户线程,而非阻塞 I/O 会立马返回一个错误,不会阻塞当前用户线程。同步和
异步是指,内核的 I/O 拷贝实现,当数据准备就绪后,需要将内核空间的数据拷贝至用户空间,
如果是同步 I/O 那么用户线程会等待拷贝的完成,而异步 I/O 则这个拷贝过程用户线程该干嘛可
以去干吗,当内核拷贝完毕之后会“通知”用户线程。
BIO,NIO,AIO
BIO: 同步阻塞
NIO:同步非阻塞
AIO:异步非阻塞
异常处理
throw 和 throws 的区别?
throws 用来声明一个方法可能抛出的所有异常,表示谁调用都要使用 try-catch 进行异常处理而
throw 则是直接抛出一个具体异常
final、finally、finalize 有什么区别?
final 可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被
重写、修饰变量表示该变量是一个常量不能被重新赋值。
在 try-catch 中不管有没有异常都要执行 finally 中的内容
finalize 属于所有类父类 Object 类的方法,该方法一般由垃圾回收器执行,当调用 system.gc()
时由垃圾回收器调用 finalize 执行
try-catch-finally 中哪个部分可以省略??
可以省略 catch 或者 finally。catch 和 finally 不可以同时省略。
原因:try 可以自己处理运行异常,try+catch 可以处理运行时 + 普通异常,当我们处理普通异常
省略 catch 编译不通过会报错但是当我们处理运行时异常则可以直接使用 try.
注:如果 catch 中 return 了,finally 会在 return 前执行
java 泛型
使用了迭代器就可以不用关注容器的内部细节,用同样的方式遍历不同类型的容器。(泛型可以修饰
类、方法、接口、变量。)
List<Object> 和 List<?> 有什么区别?
答:List<?> 可以容纳任意类型,只不过 List<?> 被赋值之后,就不允许添加和修改操作了;而
List<Object> 和 List<?> 不同的是它在赋值之后,可以进行添加和修改操作
List 和 List<Object> 的区别是什么?
答:List 和 List<Object> 都能存储任意类型的数据,但 List 和 List<Object> 的唯一区别就是,List不会触发编译器的类型安全检查,比如把 List<String> 赋值给 List 是没有任何问题的,但赋值给List<Object> 就不行
泛型的工作原理是什么?为什么要有类型擦除??
答:泛型是通过类型擦除来实现的,类型擦除指的是编译器在编译时,会擦除了所有类型相关的信息,比如 List 在编译后就会变成 List 类型,这样做的目的就是确保能和 Java 5 之前的版本(二进制类库)进行兼容。
说说反射的用途及实现原理,Java 获取反射的三种方法
Java 获取反射的三种方法:
第一种,使用 Class.forName 静态方法。
第二种,使用类的.class 方法
第三种,使用实例对象的 getClass() 方法。
讲讲类的实例化顺序,比如父类静态数据,构造函数,子类静态数据,构造函数。
所以,类实例化顺序为: 父类静态代码块/静态域->子类静态代码块/静态域 -> 父类非静态代码块 -> 父类构造器 -> 子类非静态代码块 -> 子类构造器
Java 创建对象有几种方式
Java 创建对象有 5 种方式
用 new 语句创建对象。
使用反射,使用 Class.newInstance()创建对象/调用类对象的构造方法——
Constructor
调用对象的 clone()方法。
运用反序列化手段,调用 java.io.ObjectInputStream 对象的 readObject()方法.
谈谈 Java 的异常层次结构
从前从前,有位老人,他的名字叫 Throwable,他生了两个儿子,大儿子叫
Error,二儿子叫 Exception。
Error
表示编译时或者系统错误,如虚拟机相关的错误,OutOfMemoryError 等,
error 是无法处理的。
Exception
代码异常,Java 程序员关心的基类型通常是 Exception。它能被程序本身可以
处理,这也是它跟 Error 的区别。
它可以分为 RuntimeException(运行时异常)和 CheckedException(可
检查的异常)。
静态内部类与非静态内部类有什么区别
静态内部类可以有静态成员(方法,属性),而非静态内部类则不能有静态成员(方
法,属性)。
静态内部类只能够访问外部类的静态成员和静态方法,而非静态内部类则可以访问
外部类的所有成员(方法,属性)。
实例化静态内部类与非静态内部类的方式不同
调用内部静态类的方法或静态变量,可以通过类名直接调用
String s 与 new String 与有什么区别
String s
和 new String
都用于创建字符串对象,但它们之间有重要的区别:
-
字符串池(String Pool)的使用:
String s = "Hello";
:这种方式会尝试将字符串放入字符串池中。如果字符串池中已经存在相同内容的字符串(例如,“Hello”),则会重用现有的字符串对象,而不会创建新的对象。这可以节省内存并提高性能。String s = new String("Hello");
:这种方式会创建一个新的字符串对象,无论字符串池中是否已存在相同内容的字符串。这种方式不利于内存管理,因为它会在堆内存中创建新的对象,即使内容相同。
-
可变性:
String s
对象是不可变的。一旦创建,它的内容不能被修改。任何修改字符串的操作都会创建一个新的字符串对象。new String
创建的字符串对象是可变的。你可以使用一些方法(如concat
、substring
)来修改这个字符串对象的内容。
-
内存管理:
- 使用字符串池中的字符串常常更加节省内存,因为多个字符串变量可以共享相同的字符串对象。
- 使用
new String
创建的字符串对象会占用更多内存,因为每次都会创建一个新的对象,即使内容相同。
一般来说,推荐使用字符串池中的字符串,因为它们具有更好的性能和内存管理。只有在需要修改字符串内容的情况下才使用 new String
创建的字符串对象。
反射中,Class.forName 和 ClassLoader 的区别
Class.forName 和 ClassLoader 都可以对类进行加载。它们区别在哪里呢?
ClassLoader 负责加载 Java 类的字节代码到 Java 虚拟机中。
Class.forName 其实是调用了 ClassLoader。
所以,Class.forName 和 ClassLoader 的区别,就是在类加载的时候,
class.forName 有参数控制是否对类进行初始化。
深拷贝和浅拷贝
浅拷贝
复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的
值,另一个值都会随之变化。
深拷贝
将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变
什么是拆装箱?
拆箱:把包装类型转成基本数据类型
装箱:把基本数据类型转成包装类型