Java面试题-基础
- 1、Java语言有哪些特点?
- 2、面向对象和面向过程的区别是什么?
- 3、说说标识符的命名规则?
- 4、说说Java八种基本数据类型的大小以及他们的封装类?
- 5、instanceof关键字有什么作用?
- 6、自动装箱与自动拆箱是什么?
- 7、重载和重写有什么区别?
- 8、equals与==有什么区别?
- 9、Hashcode有什么作用?
- 10、String、StringBuffer 和 StringBuilder 的区别是什么?
- 11、ArrayList和linkedList的区别是什么?
- 12、HashMap和HashTable的区别是什么?
- 13、Collection和Collections的区别是什么?
- 14、Java的四种引用强弱软虚是什么?
- 15、泛型特点有哪些?
- 16、Java创建对象的方式有哪些?
- 17、final有哪些用法?
- 18、static都有哪些用法?
- 19、有没有可能两个不相等的对象有相同的hashcode?
- 20、深拷贝和浅拷贝的区别是什么?
- 21、3*0.1== 0.3返回值是什么?
- 22、a=a+b与a+=b有什么区别吗?
- 23、try catch finally,try里有return,finally还执行么?
- 24、说说Excption与Error?
- 25、常见的运行时异常有哪些?
- 26、常见被检查异常有哪些?
- 27、常见Error错误有哪些?
- 28、OOM遇到过哪些情况?SOF遇到过哪些情况?
- 29、简述线程、程序、进程的基本概念和关系?
- 30、Java序列化中如果有些字段不想进行序列化怎么办?
- 31、说说Java中IO流?
- 32、Java lO和NIO区别?
- 33、java反射的作用和原理?
- 34、说说List,Set,Map三者的区别?
- 35、Object有哪些常用方法?大致说一下每个方法的含义
- 36、获取一个类Class对象的方式有哪些?
- 37、ArrayList 和 LinkedList 的区别有哪些?
- 38、说一下ArrayList的特点?
- 39、有数组了为什么还要搞个 ArrayList 呢?
- 40、说说什么是 fail-fast?
- 41、说说Hashtable与HashMap 的区别?
- 42、HashMap中的key我们可以使用任何类作为key吗?
- 43、HashMap的长度为什么是2的N次方呢?
- 44、HashMap与ConcurrentHashMap 的异同
- 45、红黑树有哪几个特征?
- 46、说说你平时是怎么处理Java异常的?
- 47、JVM、JRE、JDK之间的关系?
- 48、public、protected、default、private的区别?
- 49、多态是什么?如何实现多态?
- 50、说说你对ArrayList的了解?
- 51、说说你对LinkedList的了解?
- 52、说说你对Vector的了解?
- 53、说说对Set的了解?
- 54、说说你对Map的了解?
- 55、Map的遍历方式有哪些?
- 56、说说你对HashMap的了解?
- 57、说说你对TreeMap的了解?
- 58、什么是跨平台性?原理是什么?
- 59、什么是字节码?采用字节码的最大好处是什么?
- 60、Java和C++的区别?
- 61、switch 是否能作用在 byte上?是否能作用在long或String上?
- 62、Math.round(11.5) 等于多少?Math.round(-11.5)等于多少?
- 63、float f=3.4;这句代码是否正确?
- 64、short s1 = 1; s1 = s1+1; 有错吗?short s1 = 1; s1 += 1;有错吗?
- 65、Java语言采用何种编码方案?有何特点?
- 66、Java有哪些注释?
- 67、&和&&的区别?
- 68、final有什么用?
- 69、this关键字有哪些用途?
- 70、super关键字有哪些用途?
- 71、this与super的区别?
- 72、static存在的意义是什么?
- 73、static的独特之处是什么?
- 74、static应用场景有哪些?
- 75、static使用注意事项有哪些?
- 76、break,continue,return 的区别及作用?
- 77、在Java中如何跳出当前的多重嵌套循环?
- 78、面向对象的特征有哪些方面?
- 79、什么是多态机制?
- 80、java多态的实现方式?
- 81、说说面向对象编程的五大基本原则
- 82、说说抽象类和接口的异同点?
- 83、普通类与抽象类有什么区别?
- 84、抽象类能使用 final修饰吗?
- 85、创建一个对象用什么关键字?对象实例与对象引用有何不同?
- 86、成员变量与局部变量有什么区别?
- 87、在Java中定义一个无参的默认构造方法的作用是什么?
- 88、在调用子类构造方法之前先调用父类的无参数构造方法目的是什么?
- 89、类的构造方法的作用是什么?若一个类没有声明构造方法,程序能正确执行吗?
- 90、构造方法的特性有哪些?
- 91、静态变量(也称为类变量)和实例变量有什么区别?
- 92、静态变量(类变量)和普通变量(实例变量)有什么区别?
- 93、静态方法和实例方法有什么区别?
- 94、在一个静态方法内调用一个非静态成员为什么是非法的?
- 95、什么是方法的返回值?返回值的作用是什么?
- 96、内部类是什么?
- 97、内部类有哪些类型?
- 98、内部类哪些优缺点?
- 99、内部类有哪些应用场景?
- 100、局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final?
- 101、构造器是否可被重写?
- 102、HashSet如何检查重复?
- 103、两个对象的hashCode()相同,则equals()也一定为true对吗?
- 104、说说hashCode和equals方法的关系?
- 105、为什么重写equals时必须重写hashCode方法?
- 106、说说你对hashCode()的了解?
- 107、为什么要有hashCode?
- 108、对象相等和引用相等有什么不同?
- 109、当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里是值传递还是引用传递?
- 110、为什么Java中只有值传递?
- 111、值传递和引用传递有什么区别?
- 112、JDK中常用的包有哪些?
- 113、java和javax有什么区别?
- 114、java 中10流分为几种?
- 115、Files的常用方法都有哪些?
- 116、什么是反射机制?
- 117、Java反射机制有什么优缺点?
- 118、静态编译和动态编译的区别是什么?
- 119、反射机制的应用场景有哪些?
- 120、Java获取反射的三种方法?
- 121、字符型常量和字符串常量的区别是什么?
- 122、什么是字符串常量池?
- 123、String 是最基本的数据类型吗?
- 124、String有哪些特性?
- 125、String为什么是不可变的吗?
- 126、String有什么办法可以变成可变的吗?
- 127、String 类可以被继承吗?
- 128、String str="i"与 String str=new String("i")一样吗?
- 129、String s = new String("xyz");创建了几个字符串对象?
- 130、如何将字符串反转?
- 131、数组有没有length()方法? String 有没有length()方法?
- 132、String 类的常用方法都有那些?
- 133、在使用 HashMap 的时候,用String 做 key有什么好处?
- 134、String为什么是不可变的?
- 135、String和StringBuffer、 StringBuilder的区别是什么?
- 136、Integer a=127与Integer b=127相等吗?
- 137、Integer a=128与Integer b=128相等吗?
1、Java语言有哪些特点?
- 面向对象
- 平台无关性
- 简单性
- 多线程
- 安全性
- 健壮性
- 高性能
- 分布式处理
2、面向对象和面向过程的区别是什么?
- 面向过程:基于功能或过程,强调的是动作本身,按顺序执行任务的程序设计。
- 面向对象:基于对象概念,强调数据和操作数据的方法结合,支持继承、封装、多态。
3、说说标识符的命名规则?
- 标识符的含义:在程序中我们自己定义的内容,譬如类的名字,方法名称以及变量名称等等,都是标识符。
- 命名规则:(硬性要求)标识符可以包含英文字母,0-9的数字,$以及_标识符不能以数字开头,标识符不能是Java保留关键字
- 命名规范:(非硬性要求)类名规范:首字符大写,后面每个单词首字母大写(大驼峰式)。变量名规范:首字母小写,后面每个单词首字母大写(小驼峰式)。方法名规范:同变量名。
4、说说Java八种基本数据类型的大小以及他们的封装类?
- byte:大小为8位,默认值0,封装类是
Byte
。 - short:大小为16位,默认值0,封装类是
Short
。 - int:大小为32位,默认值0,封装类是
Integer
。 - long:大小为64位,默认值0L,封装类是
Long
。 - float:大小为32位,默认值0.0f,封装类是
Float
。 - double:大小为64位,默认值0.0d,封装类是
Double
。 - char:大小为16位,默认值’\u0000’(即空字符),封装类是
Character
。 - boolean:大小不明确(依赖于虚拟机实现)有一个默认值false,封装类是
Boolean
。
5、instanceof关键字有什么作用?
instanceof
关键字用于检查一个对象是否是指定类或其子类的实例,返回true
或false
。
6、自动装箱与自动拆箱是什么?
自动装箱:基本类型自动转换为对应的包装类型。例如:
- 将
int
转换为Integer
- 将
double
转换为Double
- 将
boolean
转换为Boolean
自动拆箱:包装类型自动转换为对应的基本类型。例如:
- 将
Integer
转换为int
- 将
Double
转换为double
- 将
Boolean
转换为boolean
7、重载和重写有什么区别?
重载(Overloading):
- 发生在同一个类中。
- 方法名相同,参数列表不同(类型、个数、顺序)。
- 返回类型可以不同,不影响重载判断。编译时多态。
重写 (Overriding):
- 发生在父子类之间。
- 方法名、参数列表相同。
- 返回类型必须相同或为子类型(Java 5及之后)。
- 访问权限不能比父类方法更严格。
- 运行时多态。
8、equals与==有什么区别?
==
:比较两个对象的引用或基本数据类型的值是否相同。
equals
:默认比较对象的引用,可被重写来比较对象的内容。
9、Hashcode有什么作用?
hashCode()
方法的作用是返回对象的哈希码,主要用于优化信息存储结构,如哈希表(HashMap
、 HashSet
等),通过哈希码来快速定位对象的存储地址,提高数据访问效率。在Java中相等的对象必须有相同的哈希码。
10、String、StringBuffer 和 StringBuilder 的区别是什么?
- String:不可变字符序列,每次修改都会生成新的字符串对象,效率较低于 StringBuffer 和 StringBuilder,适用于少量的字符串操作。
- StringBuffer:可变字符序列,线程安全,适用于多线程环境中的字符串操作,但线程安全特性使其效率略低于 StringBuilder。
- StringBuilder:可变字符序列,非线程安全,效率高于StringBuffer,适用于单线程环境中的大量字符串操作。
11、ArrayList和linkedList的区别是什么?
- ArrayList:
- 基于动态数组实现,支持随机访问。
- 插入、删除操作需要数组拷贝,效率较低。
- 扩容操作成本高,需复制整个数组。
- LinkedList:
- 基于双向链表实现。
- 插入、删除操作效率高,不需要移动其他元素。
- 不支持高效的随机访问。
12、HashMap和HashTable的区别是什么?
- 线程安全:
HashMap
:非线程安全。HashTable
:线程安全,通过方法同步。
- 性能:
HashMap
:因非线程安全,性能较高。HashTable
:线程安全导致性能较低。
- 键和值的null:
HashMap
:允许一键为null,多个值为null。HashTable
:键和值都不能为null。
- 遍历:
HashMap
:使用Iterator
遍历。HashTable
:使用Enumerator
或Iterator
遍历。
- 继承:
HashMap
:继承自AbstractMap
类。HashTable
:继承自Dictionary
类。
13、Collection和Collections的区别是什么?
Collection包结构
java.util.Collection
是Java集合框架的根接口,它下面有几个主要的子接口:
- List : 一个有序集合(如
ArrayList
、LinkedList
)。 - Set:一个无重复元素的集合(如
HashSet
、TreeSet
)。 - Queue:一个用于队列操作的集合(如
LinkedList
、PriorityQueue
)。
Collections的区别
- Collection:是一个接口,提供了对集合对象进行基本操作的通用接口。
- Collections:是一个包含有关集合操作的静态方法的工具类,如排序(
sort
)、搜索(search
)等,它作用于 Collection 接口及其子接口的实现。
总结:Collection
是一个集合接口,定义了集合的基本操作。Collections
是一个操作集合的工具类,提供了一系列静态方法来操作集合数据。
14、Java的四种引用强弱软虚是什么?
- 强引用(Strong Reference):
- 最常见的引用类型,如
Object obj = new Object();
。只要强引用存在,垃圾回收器永远不会回收被引用的对象。
- 最常见的引用类型,如
- 软引用(Soft Reference):
- 通过
SoftReference
类实现。软引用的对象在系统将要发生内存溢出之前,会将这些对象列入回收范围进行二次回收。适用于缓存实现。
- 通过
- 弱引用(Weak Reference):
- 通过
WeakReference
类实现。弱引用的对象只能生存到下一次垃圾收集发生之前。无论内存是否足够,都会被回收。
- 通过
- 虚引用(Phantom Reference):
- 通过
PhantomReference
类实现。虚引用的对象完全不会影响其生命周期,仅仅提供了一种确保对象被 finalize后,能做某些事情的机制。
- 通过
15、泛型特点有哪些?
- 类型安全:泛型提供编译时类型检查,避免运行时的类型转换错误。
- 消除类型强转:使用泛型后,不需要进行显式的类型转换。
- 代码重用:泛型使得代码对不同类型的对象重用成为可能,提高了代码的复用性。
- 泛型方法:允许在方法级别上指定类型参数,使得方法能在多种类型上操作。
- 泛型类/接口:可以定义带有类型参数的类或接口,用于多种数据类型的操作。
- 类型擦除:泛型信息只存在于编译阶段,在运行时会被擦除,JVM看到的只是普通类和方法。
- 限制和通配符:可以使用 extends 和 super来限制泛型的类型范围,增加了泛型的灵活性。
16、Java创建对象的方式有哪些?
- 使用
**new**
关键字:最常见的方式。 - 使用
**Class.forName()**
配合**newInstance()**
:反射机制创建对象。 - 使用
**clone()**
方法:克隆一个对象。 - 使用对象序列化和反序列化:通过读取字节数据恢复为对象。
- 使用
**Constructor.newInstance()**
:反射中的Constructor创建对象。
17、final有哪些用法?
- 修饰变量:变量成为常量,初始化后不能被修改。
- 修饰方法:方法不能被子类覆盖。
- 修饰类:类不能被继承。
18、static都有哪些用法?
- 修饰变量:变量变成类的静态变量,所有实例共享。
- 修饰方法:方法可以不通过对象而直接通过类来调用。
- 静态初始化块:用于初始化类的静态变量,只执行一次。
- 静态内部类:不需要外部类的对象就可以创建的内部类。
19、有没有可能两个不相等的对象有相同的hashcode?
是的,两个不相等的对象可以有相同的hashCode
。这种情况称为哈希碰撞(HashCollision)。由于哈希码的值域(通常是int
类型的范围)有限,而实际对象的数量可能远远超出这个范围,理论上不可能为每个不同的对象分配一个唯一的哈希码。因此不同对象产生相同哈希码是可能的。
20、深拷贝和浅拷贝的区别是什么?
浅拷贝:只复制对象及其基本数据类型的值,对象内的引用类型指向同一地址。
深拷贝:复制对象及其内部所有层级的对象,完全独立,无共享引用。
21、3*0.1== 0.3返回值是什么?
3 * 0.1 == 0.3
的返回值是False
。
这是因为浮点数的精度问题,计算机在处理浮点数时可能不会得到完全精确的结果。
22、a=a+b与a+=b有什么区别吗?
- 类型转换差异:
a = a + b
需要a
和b
类型完全匹配,或能够通过类型提升自动匹配。a += b
内部包含隐式的类型转换,即如果a``和b
类型不匹配,b
的类型会被自动提升或转换为a
的类型,然后再进行赋值。
- 效率和简洁性:
- 在编写和阅读代码时,
a += b
更简洁。 - 在某些编译器优化中,
a += b
可能会略微高效,尽管这种差异通常非常小,对性能影响不大。
- 在编写和阅读代码时,
23、try catch finally,try里有return,finally还执行么?
是的,即使try
块中有return
语句,finally
块仍然会执行。finally
块设计用于确保无论try
块内发生什么(包括返回和抛出异常),都能执行清理或释放资源等操作。
24、说说Excption与Error?
Exception
和Error
都是Throwable
类的子类,它们构成了Java异常处理的两大分支。
- Throwable
- 根类,表示所有可以作为异常被抛出的类。
- Exception
- 表示程序中出现的异常情况,可以被捕获并由程序处理。
- 分为两大类:
- Checked Exception:编译时异常,必须被显式捕获或通过方法签名声明。
- Unchecked Exception(RuntimeException):运行时异常,不需要显式捕获或声明。
- Error
- 表示严重的错误,通常是程序无法处理的,如虚拟机错误和系统错误。
- 例如,
OutOfMemoryError
、StackOverflowError
等。
结构图如下:
Throwable
|——Exception
| |——IOException
| |——SQLException
| |——RuntimeException
| |——NullPointerException
| |——IndexOutOfBoundsException
| |——ArithmeticException
|——Error|——LinkageError|——VirtualMachineError| |——OutOfMemoryError| |——StackOverflowError|——AssertionError
25、常见的运行时异常有哪些?
常见的运行时异常(RuntimeException
的子类)包括:
**NullPointerException**
:当尝试使用null
对象引用执行操作时抛出。**ArrayIndexOutOfBoundsException**
:尝试访问数组时使用了非法索引(负数或超出数组大小)。**ArithmeticException**
:发生异常的算术条件,例如除以零。**ClassCastException**
:尝试将对象强制转换为不是实例的类时抛出。**IllegalArgumentException**
:向方法传递了一个不合法或不适当的参数。**IllegalStateException**
:在不合法或不适当的时间调用方法。**NumberFormatException**
:尝试将字符串转换为数字格式,但字符串不具有适当的格式。**IndexOutOfBoundsException**
:某种类型的索引(如数组、字符串或向量)超出范围时抛出。**ConcurrentModificationException**
:在对集合进行迭代时,除了迭代器自身之外的其他方式修改了集合,就会抛出此异常。
26、常见被检查异常有哪些?
被检查的异常(Checked Exception)是那些在编译时必须被显式处理(捕获或通过方法签名声明抛出)的异常。它们直接继承自Exception
类,但不包括继承自RuntimeException
的异常。一些常见的被检查异常包括:
**IOException**
:输入输出操作失败或中断时抛出,如读写文件错误。**FileNotFoundException**
:尝试打开文件失败时抛出,文件不存在等情况。**ClassNotFoundException**
:找不到类定义时抛出。**SQLException**
:处理数据库时发生错误时抛出。**MalformedURLException**
:指定了未知协议或格式错误的 URL 时抛出。**InterruptedException**
:线程被另一个线程中断时抛出。**NoSuchMethodException**
:请求的方法不存在时抛出。**SAXException**
:处理XML文件错误时抛出。**ParseException**
:解析日期、时间等格式出错时抛出。
27、常见Error错误有哪些?
**OutOfMemoryError**
: JVM没有足够的内存分配对象。**StackOverflowError**
:栈溢出,通常是由深度递归调用导致。**NoClassDefFoundError**
:找不到类定义,可能是因为类路径问题。**UnsatisfiedLinkError**
:当Java虚拟机不能找到一个本地方法的声明时抛出。**NoSuchMethodError**
:调用不存在的方法。**ExceptionInInitializerError**
:静态初始化器抛出异常或无法加载静态初始化块。**LinkageError**
:类依赖性问题,如类的不同版本之间的不兼容。**ThreadDeath**
:线程被stop()
方法杀死时抛出。**VirtualMachineError**
: JVM发生内部错误或资源不足时的超类。
28、OOM遇到过哪些情况?SOF遇到过哪些情况?
遇到的**OutOfMemoryError**
(OOM)情况:
- 堆内存溢出:对象创建过多,GC无法回收,导致堆内存耗尽。
- 永久代(元空间)溢出:加载的类信息太多或大量动态生成类,导致永久代(Java 8后为元空间)内存不足。
- 直接内存溢出:通过
ByteBuffer.allocateDirect()
分配的直接内存过多。 - 线程堆栈溢出:应用创建过多线程,每个线程分配的堆栈大小(
-Xss
)导致总体占用空间超过可用内存。 - Tomcat 内存溢出:Web应用部署过多或单个应用占用内存过大,超出Tomcat分配的内存限制。
遇到的**StackOverflowError**
(SOF)情况:
- 深度递归调用:递归调用层次太深,超出了JVM分配给线程堆栈的容量。
- 大量局部变量:方法中定义了大量局部变量,增加了每个方法帧的大小,导致堆栈快速耗尽。
- 无限循环或递归:无终止条件的递归或循环,使得方法不断调用自身或其他方法。
面试官追问如何解决?通常这些情况通常需要通过代码优化、合理配置JVM参数、监控和分析堆栈/内存使用情况来解决。
29、简述线程、程序、进程的基本概念和关系?
程序:是指令和数据的集合,存储在磁盘上的文件,是静态的代码。
进程:程序的一次执行过程,是系统资源分配的基本单位。它拥有独立的内存空间,包含代码、数据以及执行的上下文。
线程:是进程中的执行单元,是CPU调度的基本单位。一个进程可以包含多个线程,它们共享进程的资源但能够并行执行任务。
关系:
- 程序是静态的代码和数据的集合,不具备运行的能力。
- 进程是程序运行的实例,提供必要的资源和环境。
- 线程是进程内部的执行单元,实现程序的并发执行。
简而言之程序是死的,进程是程序的活体表现,线程是进程的执行路径。一个程序可以被多个进程加载执行,一个进程可以包含多个线程共同完成任务,实现并发。
30、Java序列化中如果有些字段不想进行序列化怎么办?
如果不希望某些字段被序列化,可以将这些字段声明为transient
。使用transient
关键字修饰的字段不会被序列化到输出流中,因此在反序列化时,这些字段将被忽略,其值会被设置为类型的默认值(例如,数值型为0,对象引用为null)。
31、说说Java中IO流?
IO流用于读取和写入数据,主要分为四大类,根据数据类型分为字节流和字符流,根据流向分为输入流和输出流:
- 字节流:
- 输入流:
InputStream
是所有字节输入流的父类,用于从源读取字节数据。 - 输出流:
OutputStream
是所有字节输出流的父类,用于向目的地写出字节数据。
- 输入流:
- 字符流:
- 输入流:
Reader
是所有字符输入流的父类,用于从源读取字符数据。 - 输出流:
Writer
是所有字符输出流的父类,用于向目的地写出字符数据。
- 输入流:
主要流类
- 字节流类:
FileInputStream
、FileOutputStream
、BufferedInputStream
、BufferedOutputStream
等。 - 字符流类:
FileReader
、FileWriter
、BufferedReader
、BufferedWriter
等。
特殊流类
- 转换流:
InputStreamReader
和OutputStreamWriter
转换流可以将字节流和字符流进行转换。 - 序列化流:
ObjectInputStream
和ObjectOutputStream
用于对象的序列化和反序列化。 - 打印流:
PrintStream
和PrintWriter
提供了打印各种数据值表示的便捷方法。
使用场景
- 字节流:适用于处理原始数据,如二进制文件、图片、音频视频等。
- 字符流:适用于处理文本数据。
32、Java lO和NIO区别?
区别:
- 基本概念:
- IO:面向流(Stream Oriented),阻塞式IO。每次从流中读取或写入数据时,都会阻塞直到有数据处理。
- NIO:面向缓冲区(Buffer Oriented),非阻塞式IO。通过通道(Channel)和缓冲区(Buffer)交换数据,支持非阻塞模式和选择器(Selector)
- 阻塞与非阻塞:
- IO:基于流模型实现,进行读写操作时会一直阻塞,直到操作完成。
- NIO:可以设置为非阻塞模式,当没有数据可读或写时,线程可以做其他任务。
- 数据处理单位:
- IO:以流的方式处理数据,直接对流进行读写。
- NIO:以块(Buffer)的方式处理数据,数据首先读取到缓冲区,再从缓冲区进行处理。
- 选择器(Selectors):
- IO:不支持。
- NIO:支持选择器,一个线程可以管理多个输入和输出通道。
- 使用场景:
- IO:适用于连接数目比较小且固定的架构,这种方式可以直接对流进行处理。
- NIO:适用于连接数目多且动态变化的架构,如高性能网络服务器。
- 性能和可伸缩性:
- IO:简单易用,但在高并发情况下性能和可伸缩性较差。
- NIO:在处理并发连接和高速数据读写时具有更高的性能和可伸缩性。
33、java反射的作用和原理?
反射的作用
Java反射机制允许程序在运行时取得任何类的内部信息,并能操作任意对象的内部属性及方法。它的主要作用包括:
- 在运行时判断任意一个对象所属的类:反射机制可以通过对象获得类的信息,如类的方法、字段、构造器等。
- 在运行时构造任意一个类的对象:可以通过反射机制创建对象,而不需要通过
new
关键字实例化。 - 在运行时判断任意一个类所具有的成员变量和方法:反射机制可以遍历类的字段和方法,甚至调用方法。
- 在运行时调用任意一个对象的方法:反射机制允许调用任意对象的方法,而不需要事先知道该对象的类型。
- 生成动态代理:利用反射机制生成类或接口的动态代理实现。
反射的原理
Java反射是通过类加载机制来实现的。当Java程序运行时,Java虚拟机(JVM)会将使用到的类加载到内存中,并为每个类创建一个Class
类型的对象(即类对象),用于存储类的元数据信息(如类的名称、方法信息、字段信息等)。当程序通过反射API(如Class.forName()
,obj.getClass()
,.class
语法)获取到对应的Class
对象后,就可以利用这个对象来访问类的信息和动态操作类或对象。
- 类的加载:当程序使用到某个类时,Java虚拟机将该类加载到内存中。
- 创建
**Class**
对象:加载过程中,JVM为每个类创建Class
对象,其中包含了类的元数据信息。 - 使用
**Class**
对象:程序可以通过Class
对象访问类的元数据,创建实例,访问字段和方法等。
34、说说List,Set,Map三者的区别?
List:
- 有序集合,按插入顺序存储元素。
- 允许重复的元素。
- 典型实现:
ArrayList
,LinkedList
。
Set:
- 无序集合,不保证存储顺序。
- 不允许重复的元素。
- 典型实现:
HashSet
,LinkedHashSet
,TreeSet
。
Map:
- 键值对集合,存储元素为键值对( key-value)。
- 保证键的唯一性,值可以重复。
- 不是
Collection
接口的一部分。 - 典型实现:
HashMap
,LinkedHashMap
,TreeMap
。
区别:
- 存储结构: List是序列集合,Set是不重复元素的集合,Map是键值对的映射。
- 元素唯一性:List 允许重复元素,Set不允许重复元素,Map的键不允许重复。
- 存储顺序:List保持元素的插入顺序,Set通常不保证顺序(除
LinkedHashSet
),Map不保证键值对的顺序(除LinkedHashMap
)。
35、Object有哪些常用方法?大致说一下每个方法的含义
public final Class<?> getClass()
:- 返回此对象的运行时类。
public int hashCode()
:- 返回对象的哈希码值,用于哈希表。
public boolean equals(Object obj)
:- 指示其他某个对象是否与此对象"相等”。
protected Object clone() throws CloneNotSupportedException
:- 创建并返回此对象的一个副本。
public String toString()
:- 返回对象的字符串表示,通常是对象的类名加上其哈希码的无符号十六进制表示。
public final void notify()
:- 唤醒在此对象监视器上等待的单个线程。
public final void notifyAll()
:- 唤醒在此对象监视器上等待的所有线程。
public final void wait(long timeout) throws InterruptedException
;- 使当前线程等待直到另一个线程调用此对象的
notify()
方法或notifyAll()
方法,或者超过指定的时间量。
- 使当前线程等待直到另一个线程调用此对象的
public final void wait(long timeout, int nanos) throws InterruptedException
:- 使当前线程等待直到另一个线程调用此对象的
notify()
方法或notifyAll()
方法,或者其他某个线程中断当前线程,或者已经过了指定的实际时间。
- 使当前线程等待直到另一个线程调用此对象的
protected void finalize() throws Throwable
:- 当垃圾收集器决定不存在对该对象的更多引用时,由垃圾收集器调用此方法。用于资源释放等清理操作。已在Java 9中被标记为过时。
36、获取一个类Class对象的方式有哪些?
- 使用
.class
语法,如String.class
。 - 通过对象调用
getClass()
方法,如"hello".getClass()
。 - 使用
Class.forName()
静态方法,如Class.forName("java.lang.String")
。
37、ArrayList 和 LinkedList 的区别有哪些?
- 内部结构:
ArrayList
基于动态数组。LinkedList
基于双向链表。
- 性能:
ArrayList
优于LinkedList
在随机访问数据。LinkedList
优于ArrayList
在添加和删除操作。
- 内存占用:
ArraylList
更紧凑,但在扩容时需要重新分配内存并复制。LinkedList
为每个元素使用更多内存(因节点引用)。
38、说一下ArrayList的特点?
- 基于动态数组实现:内部使用可扩容的数组结构,便于索引访问和存储。
- 随机访问效率高:提供快速的随机访问性能,通过索引直接访问元素。
- 添加、删除元素成本较高:除了尾部添加外,插入或删除元素可能需要移动多个元素,效率较低。
- 动态扩容:当添加元素超过当前容量时,自动扩容,但扩容操作涉及数组复制,可能影响性能。
- 不是线程安全的:多线程环境下,需要外部同步。
39、有数组了为什么还要搞个 ArrayList 呢?
- 动态扩容:数组大小固定,一旦创建不能改变。
ArrayList
可以自动扩展和缩减大小,提供了更大的灵活性。 - 便捷的操作:
ArrayList
提供了大量方法来进行元素的插入、删除、遍历等操作,使用起来更加方便。 - 泛型支持:
ArrayList
支持泛型,可以在编译时检查元素类型,提高代码的安全性和可读性。 - 集合框架的一部分:
ArrayList
是Java 集合框架的一部分,可以与其他集合类型(如Set
、Map
)无缝集成,提供了统一的编程接口。
40、说说什么是 fail-fast?
Fail-fast 是一种错误检测机制,指的是在操作集合(如列表、集合等)时,如果检测到集合在迭代过程中被结构性地修改(例如添加、删除元素),则立即抛出ConcurrentModificationException
异常。这种机制旨在快速失败,避免不确定的行为和潜在的错误。Java集合框架中的很多实现(如ArrayList
、HashMap
)都采用了fail-fast 机制。它主要通过在迭代器内部维护一个修改计数器(modCount)来实现,每次迭代前检查计数器是否有变化,以保证迭代的一致性和安全性。
41、说说Hashtable与HashMap 的区别?
- 线程安全:
Hashtable
是线程安全的,HashMap
不是。 - 性能:因为线程安全,
Hashtable
性能较HashMap
低。 - 空值:
Hashtable
不允许键或值为null
,HashMap
允许一个键为null
和多个值为null
。 - 遍历:
Hashtable
继承自Dictionary
类,HashMap
是Java Collections Framework的一部分。
42、HashMap中的key我们可以使用任何类作为key吗?
是的,可以使用任何类作为HashMap
中的key,但为了确保HashMap
正确地存储和检索键值对,有两个重要条件需要满足:
- 重写
**equals()**
方法:确保当两个键对象逻辑上等价时,可以返回true
。 - 重写
**hashCode()**
方法:确保逻辑上等价的键对象产生相同的哈希码。
如果这两个方法没有被适当地重写,HashMap
可能无法准确地识别键对象,导致数据存取错误。
43、HashMap的长度为什么是2的N次方呢?
HashMap的长度是2的N次方主要是为了优化索引计算的效率。这样设计可以使得索引的计算通过位运算(hash & (length-1)
)来完成,这比模运算更高效。同时,这种长度能够帮助哈希表中的元素更均匀地分布,减少哈希冲突,提高了HashMap的整体性能。
44、HashMap与ConcurrentHashMap 的异同
相同点:
- 都实现了
Map
接口,用于存储键值对。 - 内部结构都利用了哈希表。
不同点:
- 线程安全:
HashMap
是非线程安全的,适用于单线程环境。ConcurrentHashMap
是线程安全的,适用于多线程环境。
- 性能:
- 由于线程安全的实现方式不同、
ConcurrentHashMap
在多线程环境下性能优于使用Collections.synchronizedMap(new HashMap<>())
包装的HashMap
。
- 由于线程安全的实现方式不同、
- 内部实现:
HashMap
允许一个null
键和多个null
值。ConcurrentHashMap
不允许null
键和null
值,因为并发读取时无法区分返回值null
是映射值本身为 null还是键不在映射中。
- 并发控制:
HashMap
在扩容时直接对整个哈希表进行扩容。ConcurrentHashMap
使用分段锁(在Java 8以后是通过分散数组、链表和红黑树的结构,以及CAS操作和synchronized
来保证线程安全),减少了锁竞争,提高了效率。
45、红黑树有哪几个特征?
红黑树的五个特征:
- 每个节点是红色或黑色。
- 根节点是黑色。
- 所有叶子(NI节点)都是黑色。
- 每个红色节点的两个子节点都是黑色(红色节点不能连续)。
- 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
46、说说你平时是怎么处理Java异常的?
- 明确异常类型:区分检查型异常和运行时异常。
- 合理使用 try-catch:捕获具体能处理的异常,避免捕获
Exception
或Throwable
。 - 合理利用finally:确保资源在异常发生时也能被正确释放。
- 避免空catch块:即使暂时不处理异常,也应记录或注释说明原因。
- 使用throws声明:对于无法立即处理的检查型异常,通过方法签名向上抛出。
- 异常链:在捕获异常后抛出新异常时,保留原始异常信息。
- 自定义异常:根据需要合理定义业务异常类,提高异常的可识别性。
- 避免异常用于控制流程:异常应用于异常情况处理,不应作为常规控制流程的一部分。
47、JVM、JRE、JDK之间的关系?
- JVM(Java Virtual Machine):是一个抽象的计算机,提供运行Java字节码的环境,使得Java程序能够在任何平台上运行不受限制。
- JRE(Java Runtime Environment) :包括JVM和运行Java程序所需的核心库及其他组件。它是运行已经开发的Java程序所需要的环境。
- JDK(Java Development Kit):包含了JRE以及开发Java程序所需的编译器、工具和库(比如javac)。JDK是用于开发Java应用程序的软件开发工具包。
关系:JDK用于开发Java程序,包括JRE来运行编译后的程序,而JRE包含JVM来提供一个平台无关的运行环境。简而言之,JDK > JRE > JVM,其中JDK是包含JRE的,而JRE是包含JVM的。
48、public、protected、default、private的区别?
在Java中,public
、protected
、default
(无修饰符)和private
关键字用于指定类成员(方法和变量)的访问级别:
- public:成员可被任何其他类访问。
- protected:成员可被同一包内的类以及所有子类访问。
- default(无修饰符):成员只能被同一包内的类访问。
- private:成员只能被其所在类访问。
这些修饰符从宽松到严格排序为:public > protected > default > private,它们定义了类成员在不同上下文中的可见性和访问性。
49、多态是什么?如何实现多态?
多态是面向对象编程中的一个核心概念,指的是同一操作作用于不同的对象时,可以有不同的解释和表现。在Java中,多态可以通过继承(inheritance)和接口(interfaces)来实现,具体体现为:
- 方法重写(Override):子类重写父类中的方法,实现运行时多态。
- 接口实现(Implements):类通过实现接口并提供接口方法的具体实现,同样实现运行时多态。
- 向上转型(Upcasting):子类对象直接赋值给父类引用,通过父类引用调用重写方法,实现多态。
50、说说你对ArrayList的了解?
ArrayList
是Java中的一个可调整大小的数组实现,属于java.util
包。它允许存储任意类型的对象(包括null
),并且可以动态地增加和减少其容量。ArrayList
提供了快速的随机访问能力(即按索引访问元素),但在列表中间插入或删除元素的速度相对较慢,因为这可能需要移动现有元素。ArrayList
实现了List
接口,因此它支持所有的列表操作,如添加、删除、清空列表以及支持迭代器等。由于其内部是通过数组实现的,当元素被添加到ArrayList
中而数组容量不足时,其内部会自动扩容以容纳更多的元素。
51、说说你对LinkedList的了解?
LinkedList
是Java中的一个双向链表实现,属于java.util
包。它允许存储任意类型的对象,包括null
。与ArrayList
相比,LinkedList
提供了更高效的元素插入和删除操作,因为这些操作通常只需改变节点的指针,而不需要移动其他元素。然而,LinkedList
在随机访问元素时性能较低,因为它需要从头或尾开始遍历链表来访问特定索引的元素。
LinkedList
实现了List
接口,因此支持所有列表操作,如添加、删除和遍历元素。此外,LinkedList
还实现了Deque
接口,使其能够被用作双端队列进行元素的入队和出队操作。由于其链表的特性,LinkedList
在实现栈、队列或双端队列时是一个好的选择。
52、说说你对Vector的了解?
Vector
是Java中的一个动态数组实现,属于java.util
包。与ArrayList
类似,Vector
也支持动态扩容,可以根据需要增加和减少容量来存储任意类型的对象。不同之处在于Vector
是同步的(synchronized),这意味着它是线程安全的。因此,在多线程环境中,当多个线程同时访问Vector
实例时,它保证了数据的一致性和完整性。
由于其线程安全的特性,Vector
的性能可能会比非同步的ArrayList
稍低。Vector
提供了类似于ArrayList
的API,支持快速随机访问、元素的添加、删除和遍历等操作。然而,在新的Java应用中,通常推荐使用Collections.synchronizedList
或CopyOnWriteArrayList
来获得线程安全的列表,而不是直接使用Vector
。
53、说说对Set的了解?
Set
是Java中的一个接口,属于java.util
包,代表一个不包含重复元素的集合。它是Java集合框架(Java Collections Framework)的一部分,用于存储一组唯一的元素,不保证元素的顺序。Set
接口主要有以下几个实现:
- HashSet:基于哈希表实现,提供快速的插入、查找和删除操作,不保证元素的迭代顺序。
- LinkedHashSet:基于哈希表和链表实现,维护元素的插入顺序,性能略低于
HashSet
。 - TreeSet:基于红黑树实现,元素按照自然顺序或自定义比较器排序,提供有序的集合操作。
Set
接口支持基本操作如添加、删除元素以及判断元素是否存在。由于Set
不允许重复元素,添加已存在的元素会被忽略。Set
常用于去除重复数据、集合运算(如并集、交集、差集)等场景。
54、说说你对Map的了解?
Map
是Java中的一个接口,表示一个键(Key)到值(Value)的映射。它不能包含重复的键,每个键最多只能映射到一个值。这个接口主要用于存储键值对,键和值都可以是任意类型的对象,包括null
。Map
接口的实现类有多种,包括但不限于:
- HashMap:基于哈希表实现,提供快速的查找、插入和删除操作,不保证映射的顺序。
- LinkedHashMap:基于哈希表和链表实现,维护元素的插入顺序或访问顺序。
- TreeMap:基于红黑树实现,按照键的自然顺序或者构造时提供的
Comparator
进行排序。 - Hashtable:和
HashMap
类似,但是它是同步的,不允许键或值为null
。
Map
提供的操作包括添加、删除键值对,检查键或值是否存在,以及访问键集、值集或键值对集。它是处理键值对数据的重要数据结构,广泛应用于需要快速查找数据的场景。
55、Map的遍历方式有哪些?
遍历Map
的方法主要有以下几种:
- 使用
**keySet()**
遍历键集:先获取Map
的所有键的集合,然后通过遍历这些键来访问每个键对应的值。 - 使用
**values()**
遍历值集:直接获取Map
中所有值的集合,遍历这个集合可以访问所有的值,但无法直接获取对应的键。 - 使用
**entrySet()**
遍历键值对:获取Map
中所有键值对的集合,然后遍历这个集合。每个元素都是Map.Entry
对象,可以通过它获取键和对应的值。 - 使用Java 8的
**forEach**
方法:利用forEach
方法和Lambda表达式直接对键值对进行操作,更加简洁高效。 - 使用迭代器(
**lterator**
)遍历:通过keySet()
、values()
或entrySet()
获取迭代器,然后使用迭代器进行遍历。
56、说说你对HashMap的了解?
HashMap
是Java中基于哈希表实现的Map
接口的一个类。它存储键值对,并允许使用nul1值和null
键。HashMap
不保证映射的顺序;随着时间的推移,这个顺序可能会改变。它不是线程安全的,如果多线程同时访问HashMap
且至少有一个线程修改了映射,则必须外部同步。HashMap
提供了常数时间的性能 (O(1)
)对于基本操作,如获取和插入元素,假设哈希函数将元素适当地分散在桶中。它通过使用哈希码为每个键值对分配一个桶来实现快速查找和插入操作。
57、说说你对TreeMap的了解?
TreeMap
是Java中基于红黑树实现的Map接口的一个类。它存储键值对,并且按照键的自然顺序(或者根据构造TreeMap
时提供的Comparator
所指定的顺序)对键进行排序,从而保证了元素的有序性。TreeMap
不允许使用null
键(如果使用自然排序),但允许使用null值。与HashMap
相比,TreeMap
提供了一致的O(log n)
时间性能对于包含。个元素的映射的查找、插入和删除操作,因为红黑树是一种自平衡的二叉查找树。TreeMap
适合于需要按顺序访问键的场景,如实现范围查找和排序操作。
58、什么是跨平台性?原理是什么?
- 跨平台性指的是Java程序能够在不同的操作系统上运行而不需要做任何修改。这种能力基于"编写一次,处处运行”(Write Once,Run Anywhere,WORA)的原则。
- Java的跨平台性原理是通过Java虚拟机(JVM)实现的。具体来说:
- 编译为字节码:Java源代码被编译成一种中间形式的字节码(.class文件),而不是直接编译为特定平台的机器码。
- JVM执行字节码:JVM是一个运行在宿主机操作系统上的软件,它能够加载字节码并执行它们。每个平台(如Windows、Linux、macOS)都有适配该平台的JVM实现。
- 平台无关性:由于JVM负责字节码与特定平台之间的交互,因此Java程序不需要为了在不同平台上运行而做出任何修改。
59、什么是字节码?采用字节码的最大好处是什么?
- 字节码是一种中间代码(Intermediate Code)形式,主要用于Java和一些其他语言(如Scala)。它介于高级语言代码和机器语言代码之间,是由Java编译器从Java源代码编译得到的,存储于
.class
文件中。字节码是独立于机器的代码,需要通过Java虚拟机(JVM)来解释执行或编译执行(即川编译)。 - 采用字节码的最大好处是跨平台性。字节码可以在任何安装了兼容的JVM的平台上运行,实现了“编写一次,处处运行”(Write Once,RunAnywhere,WORA)的目标。这种设计极大地提高了软件的可移植性,开发者无需为每个目标平台重新编译代码,降低了开发和维护成本。
60、Java和C++的区别?
Java和C++主要区别包括:
- 内存管理:Java具有自动垃圾回收机制,而C++需要手动管理内存。
- 平台独立性:Java程序编译为平台无关的字节码,通过JM在不同平台上运行。C++编译为特定平台的机器码,直接由操作系统执行。
- 运行环境:Java需要JVM作为运行时环境。C++程序直接在操作系统上运行,无需虚拟机。
- 语言特性:C++支持运算符重载、多重继承和指针操作,提供更接近硬件的编程能力。Java设计上简化了这些特性,以减少程序复杂性和提高安全性。
- 性能:C++通常提供更高的性能,适合系统级开发,如操作系统和游戏引擎。Java在跨平台和网络应用中更为流行,牺牲了一定的性能以获得更高的移植性和开发效率。
- 错误处理:Java使用异常处理机制来处理错误,C++既支持异常处理也支持传统的错误处理代码。
- 标准库:Java提供了丰富的标准库,尤其是在图形界面和网络编程方面。C++标准库主要集中在容器、算法和函数对象等。
61、switch 是否能作用在 byte上?是否能作用在long或String上?
- byte:可以,
switch
可以作用在byte
类型上。 - long:不可以,
switch
不能作用在long
类型上。 - String:可以,从Java 7开始,
switch
可以作用在String
类型上。
62、Math.round(11.5) 等于多少?Math.round(-11.5)等于多少?
Math.round(11.5)
等于12。Math.round(-11.5)
等于-11。
63、float f=3.4;这句代码是否正确?
不正确。字面量3.4默认是double
类型的,直接赋值给float
变量会导致编译错误。应该声明为float
类型,方法是在数字后加上f
或F
,如:
float f = 3.4f;
64、short s1 = 1; s1 = s1+1; 有错吗?short s1 = 1; s1 += 1;有错吗?
- 对于
short s1 = 1; s1 = s1 + 1;
,有错误。因为1
是int
类型,s1+1
的结果也会被提升为int
类型,不能直接赋值给short
类型的变量s1
,除非进行强制类型转换。 - 对于
short s1 = 1; s1 += 1;
,没有错误。+=
操作符会自动处理类型转换的问题,它会将右侧表达式的结果转换为左侧变量的类型,然后再赋值,因此不会有编译错误。
65、Java语言采用何种编码方案?有何特点?
Java语言采用Unicode编码方案。特点包括:
- 跨平台一致性:确保Java程序在不同平台上字符表示的一致性。
- 国际化支持:支持多种语言和字符集,方便构建国际化应用。
- 固定长度:在Java中,
char
类型用于表示一个16位的Unicode字符(UTF-16编码)。
66、Java有哪些注释?
Java有三种注释方式:
- 单行注释:使用
//
,注释从//
开始到行末。 - 多行注释:使用
/*
和*/
包围,可以跨越多行。 - 文档注释:使用
/**
和*/
包围,用于生成JavaDoc文档。
67、&和&&的区别?
&
是位运算符,用于按位与操作;在布尔逻辑中,也可以作为逻辑与操作,但它会对两边的表达式都进行求值。
&&
是逻辑与运算符,仅用于布尔逻辑中。它具有短路特性,即如果第一个操作数为false
,则不计算第二个操作数。
68、final有什么用?
final
关键字在Java中有三种主要用途:
- 声明常量:用于变量,表示变量的值一旦被初始化后就不能被改变。
- 防止继承:用于类,表示类不能被继承。
- 防止重写:用于方法,表示方法不能被子类重写。
69、this关键字有哪些用途?
this
关键字在Java中主要有以下用途:
- 引用当前对象:在方法内部,
this
用来引用当前对象的实例。 - 调用其他构造器:在构造器中,
this()
用来调用同一个类的其他构造器。 - 传递当前对象:可以将
this
作为参数传递给其他方法或构造器。 - 解决命名冲突:用
this
来区分成员变量和局部变量,特别是当它们名称相同时。
70、super关键字有哪些用途?
super
关键字在Java中主要有以下用途:
- 访问父类的成员:用
super
来访问父类中被子类覆盖(重写)的方法和变量。 - 调用父类的构造器:在子类的构造器中,
super()
用来调用父类的构造器。如果没有显式调用,Java编译器会自动插入对父类无参构造器的调用。 - 解决命名冲突:当子类和父类有同名的成员时,可以用
super
来引用父类的成员。
71、this与super的区别?
区别:
this
引用的是当前对象的实例,用于访问当前类的成员(变量、方法)、调用当前类的其他构造器、或者将当前对象传递给其他方法。super
引用的是当前对象的父类实例,用于访问父类的成员(变量、方法)和调用父类的构造器,特别是访问被子类覆盖的成员或调用父类的构造器。
简而言之,this
用于指代当前类的上下文,而super
用于指代父类的上下文。
72、static存在的意义是什么?
static
存在的主要意义在于:
- 共享:
static
关键字用于声明类级别的变量和方法,使得它们可以在没有创建类实例的情况下被访问。static
变量被类的所有实例共享。 - 工具方法:使用
static
方法可以创建无需对象实例即可调用的工具方法,例如Math
,sqrt(double)
。 - 内存管理:
static
变量和方法属于类,而非类的实例,因此它们在内存中只有一份,有助于减少内存使用。 - 初始化代码块:通过
static
初始化代码块,可以在类被加载时执行初始化操作。
73、static的独特之处是什么?
static
的独特之处在于:
- 属于类:
static
成员(变量和方法)属于类本身,而非类的实例对象。 - 内存共享:所有实例共享同一个
static
变量,static
方法可以在没有类实例的情况下直接通过类名调用。 - 初始化时机:
static
变量和static
代码块在类加载到JVM时仅初始化一次。 - 无需实例:可以不创建对象实例而直接使用类名访问
static
成员。 - 全局变量:可以用作跨实例共享的全局变量。
- 工具方法:
static
方法常用作工具或帮助方法,如Math
类中的方法。
74、static应用场景有哪些?
static
关键字的应用场景包括:
- 常量定义:使用
static final
定义类级别的常量。 - 工具/帮助方法:不依赖于对象状态的方法,如数学计算或工具方法,可以声明为
static
。 - 单例模式:使用
static
变量存储类的单一实例。 - 全局状态或资源:共享在所有实例之间的资源或状态,如配置信息。
- 静态初始化代码块:用于初始化类级别的资源或执行静态变量的初始化。
- 静态内部类:不需要外部类实例就可以创建的内部类,适合作为工具或辅助类。
75、static使用注意事项有哪些?
使用static
时的注意事项包括:
- 内存管理:
static
变量存储在静态区,随类的加载而加载,随类的消失而消失,过多使用可能增加内存负担。 - 线程安全:
static
变量如果被多个线程访问,需要考虑同步机制以避免线程安全问题。 - 生命周期:
static
成员的生命周期长于任何对象实例,它们在程序启动时被初始化,程序结束时被销毁。 - 访问限制:
static
方法内不能直接访问非静态成员。 - 设计考虑:不应滥用
static
,应当仅在表示类级别的共享数据或行为时使用。 - 测试难度:静态方法和静态状态可能增加单元测试的难度,因为它们不易被模拟或重置。
76、break,continue,return 的区别及作用?
- break:
- 作用:立即退出最近的循环(
for
、while
、do-while
)或者switch
语句。 - 区别:只影响一个循环或
switch
语句,不会退出方法。
- 作用:立即退出最近的循环(
- continue:
- 作用:立即跳过当前迭代,进入下一次循环的迭代。
- 区别:仅用于循环体内,不会影响循环外的代码或退出循环,只是提前结束当前的迭代。
- return:
- 作用:退出当前方法,并可选择返回一个值(对于非
void
方法)。 - 区别:
return
不仅能结束循环,还能结束整个方法的执行,并将控制权交回方法被调用的地方。如果方法声明了返回类型,return
后应跟一个返回值。
- 作用:退出当前方法,并可选择返回一个值(对于非
简而言之,break
用于完全结束循环或switch
语句,continue
用于结束当前迭代进入下一次循环,而return
用于结束整个方法的执行。
77、在Java中如何跳出当前的多重嵌套循环?
- 可以通过使用带标签的
break
语句来跳出当前的多重嵌套循环。 - 首先给外层循环设置一个标签,然后在需要跳出循环的地方使用
break
语句并指定该标签。示例如下:
outerloop: //标签
for(int i=0;i<10; i++) {for(int j=0;j<10; j++) {if(someCondition) {break outerLoop; //跳出外层循环}}
}
在这个例子中,如果满足someCondition
条件,则通过break outerLoop;
语句跳出名为outerLoop
的外层循环。
78、面向对象的特征有哪些方面?
面向对象编程(OOP)的主要特征包括:
- 封装:隐藏对象的内部细节,只暴露必要的操作接口。
- 继承:允许新的类继承现有类的属性和方法。
- 多态性:允许不同类的对象对同一消息做出响应,但表现出不同的行为(例如,方法的重载和重写)。
- 抽象:将复杂的现实问题抽象成简单的模型,只关注与当前目标相关的部分。
79、什么是多态机制?
多态机制是面向对象编程中的一个核心概念,允许不同类的对象对同一消息(方法调用)作出不同的响应。多态主要有两种形式:
- 编译时多态(静态多态):通过方法重载实现,同一个类中存在多个同名方法,但参数列表不同。
- 运行时多态(动态多态):通过方法重写实现,子类重写父类方法。在程序运行时,根据对象的实际类型来调用相应的方法。
80、java多态的实现方式?
Java中多态的实现主要依赖于以下三个机制:
- 继承:子类继承父类的方法和属性,提供基础的类层次结构。
- 重写(Override):子类可以重写父类中的方法,提供具有相同名称和参数列表的新实现。运行时多态通过方法重写实现,JVM在运行时决定要调用的具体方法。
- 向上转型(Upcasting):在多态中,父类引用可以指向子类对象。这使得在运行时,可以通过父类引用调用实际子类的重写方法,而编译器只检查父类引用的方法。
81、说说面向对象编程的五大基本原则
面向对象编程的五大基本原则,简称为SOLID原则,包括:
- 单一职责原则(Single Responsibility Principle,SRP):一个类应该只有一个引起它变化的原因。
- 开闭原则(Open/Closed Principle,OCP):软件实体应当对扩展开放,对修改封闭。
- 里氏替换原则(Liskov Substitution Principle,LSP):子类应该能够替换它们的基类。
- 接口隔离原则(Interface Segregation Principle,ISP) :客户端不应该被迫依赖于它们不使用的接口。
- 依赖倒置原则( Dependency Inversion Principle,DIP):高层模块不应该依赖低层模块,它们都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。
82、说说抽象类和接口的异同点?
抽象类和接口在Java中都用来定义抽象类型,它们有以下主要异同点:
相同点:
- 都不能被实例化。
- 都可以包含抽象方法,即没有实现的方法。
不同点:
- 抽象类:
- 可以包含具有实现的方法。
- 可以包含成员变量。
- 一个类只能继承一个抽象类。
- 可以有构造方法。
- 抽象方法可以有任何访问修饰符。
- 接口:
- Java 8之前,接口中的方法都是抽象的,不能有实现(Java 8后,接口可以包含默认和静态方法)。
- 接口中的变量默认都是
public static final
的。 - 一个类可以实现多个接口。
- 没有构造方法。
- 接口中的方法默认是
public
的。
抽象类用于表示"is-a"关系,用于类的继承;接口更多地用于表示"has-a"能力,或特定的行为契约。
83、普通类与抽象类有什么区别?
区别如下:
- 实例化:普通类可以被直接实例化,而抽象类不能被实例化。
- 包含抽象方法:抽象类可以包含抽象方法(即没有具体实现的方法),普通类不能包含抽象方法。
- 目的:普通类主要用于创建对象和实现功能,而抽象类主要用于作为其他类的基类,定义共有的抽象方法,强制子类实现这些方法。
- 构造方法:尽管抽象类不能被实例化,它们仍然可以有构造方法,这个构造方法可以被子类通过
super
关键字调用。 - 使用场景:当你有一个包含方法实现的类时,使用普通类;当你想要定义一个模板类,其中包含一些方法声明但不实现(留给子类实现)时,使用抽象类。
84、抽象类能使用 final修饰吗?
不能,抽象类不能使用final
修饰。因为final
修饰的类不能被继承,而抽象类的目的是为了被其他类继承并实现其抽象方法。这两个修饰符的目的相互矛盾,所以不能同时使用。
85、创建一个对象用什么关键字?对象实例与对象引用有何不同?
创建对象使用new
关键字。例如,new ClassName()
会创建ClassName
类型的一个对象实例。
对象实例与对象引用的区别在于:
- 对象实例:是通过使用
new
关键字创建的,它在内存中占有实际的空间,保存着对象的状态(属性值)和行为(方法)。 - 对象引用:是指向对象实例的变量。引用本身并不存储对象实例的实际数据,而是存储了对象实例在内存中的地址。通过引用,我们可以访问和操作对象实例。
简而言之,对象实例是具体的数据和方法的集合,而对象引用是一个指向那些数据和方法所在内存地址的变量。
86、成员变量与局部变量有什么区别?
区别主要包括:
- 声明位置:成员变量声明在类中,而局部变量声明在方法内、方法参数或者代码块内。
- 生命周期:成员变量的生命周期与对象的生命周期一致,而局部变量的生命周期随着方法或代码块的执行结束而结束。
- 初始值:成员变量有默认初始值(例如,int的默认值为0,对象引用的默认值为null),局部变量没有默认初始值,必须先声明、赋值后才能使用。
- 访问修饰符:成员变量可以使用访问修饰符(如public,private等),控制其访问级别;局部变量不可以使用访问修饰符。
- 作用范围:成员变量的作用范围是整个类内部,局部变量的作用范围限于声明它的方法或代码块内部。
87、在Java中定义一个无参的默认构造方法的作用是什么?
主要作用是:
- 提供对象实例化的能力:如果一个类中没有显式地定义任何构造方法,Java编译器会自动提供一个默认的无参构造方法。但是,如果类中定义了至少一个构造方法(无论是否有参数),编译器不会提供默认构造方法。因此,手动定义一个无参构造方法确保了类可以被实例化,即使类中还有其他带参数的构造方法。
- 在子类构造方法中的调用:在继承关系中,子类的构造方法默认会调用父类的无参构造方法,如果父类中没有显式地定义这样一个构造方法,而且也没有其他构造方法,子类的构造会失败。提供一个无参构造方法可以解决这个问题。
88、在调用子类构造方法之前先调用父类的无参数构造方法目的是什么?
在调用子类构造方法之前先调用父类的无参数构造方法的目的是为了确保父类的状态被正确初始化。这一过程保证了在子类的构造方法执行之前,父类的成员变量和环境已经被设置好,确保继承体系中的对象在使用之前处于一个有效和一致的状态。
89、类的构造方法的作用是什么?若一个类没有声明构造方法,程序能正确执行吗?
类的构造方法的作用是初始化对象,为对象成员变量设置初始值,并执行任何启动构造对象时必须的步骤。如果一个类没有声明构造方法,程序仍然能正确执行,因为Java编译器会为这个类自动提供一个默认的无参数构造方法(default constructor),这个构造方法没有参数,体内也没有具体执行语句。这保证了即使没有显式定义构造方法,也能实例化对象。
90、构造方法的特性有哪些?
- 名称与类名相同:构造方法的名称必须与类名完全一致。
- 没有返回类型:构造方法不定义返回值类型,连
void
也不写。 - 初始化对象:构造方法的主要作用是初始化新创建的对象。
- 自动调用:当通过
new
关键字创建类的新实例时,构造方法会自动被调用。 - 可以重载:一个类可以有多个构造方法,彼此之间通过参数列表的不同进行区分(这被称为构造方法重载)。
- 不可以被
**static**
、**final**
、**abstract**
、**synchronized**
修饰:构造方法不能被这些关键字修饰,因为它们与构造方法的目的不兼容。
91、静态变量(也称为类变量)和实例变量有什么区别?
区别在于:
- 存储位置:静态变量存储在类的静态存储区,所有实例共享同一个静态变量;实例变量存储在堆上,每个对象有自己的独立副本。
- 访问方式:静态变量可以通过类名直接访问,无需创建对象实例;实例变量只能通过对象实例访问。
- 生命周期:静态变量的生命周期从类被加载开始,到类被卸载结束;实例变量的生命周期从对象创建开始,到对象被垃圾回收结束。
- 用途:静态变量通常用于类级别的常量或方法(如工具方法)中;实例变量用于存储对象级别的状态信息。
92、静态变量(类变量)和普通变量(实例变量)有什么区别?
- 声明:静态变量使用
static
关键字声明,普通变量不使用。 - 存储:静态变量存储在类的静态存储区,属于类级别,所有实例共享同一个静态变量;普通变量存储在堆内存的每个对象实例中,每个对象有自己的一份副本。
- 访问:静态变量可以通过类名直接访问,也可以通过对象实例访问(不推荐);普通变量只能通过对象实例访问。
- 生命周期:静态变量的生命周期从类被加载到类被卸载;普通变量的生命周期随对象的创建和垃圾回收。
- 用途:静态变量常用于表示类的公共状态或常量;普通变量用于表示对象的属性和状态。
93、静态方法和实例方法有什么区别?
- 声明:静态方法使用
static
关键字声明,实例方法不使用。 - 调用:静态方法可以通过类名直接调用,不需要创建对象实例;实例方法必须通过对象实例来调用。
- 访问变量:静态方法只能直接访问类的静态变量和静态方法;实例方法可以访问类的静态变量、静态方法以及通过对象实例访问非静态变量和方法。
- 用途:静态方法通常用于执行不依赖于对象实例的操作;实例方法用于执行与对象实例相关的操作,可以操作和修改对象实例的状态。
- 重写:静态方法不能被重写,但可以被隐藏;实例方法可以被重写。
94、在一个静态方法内调用一个非静态成员为什么是非法的?
在一个静态方法内调用一个非静态成员是非法的,因为静态方法属于类本身,而不依赖于任何特定的对象实例来执行。非静态成员(包括变量和方法)需要依赖于对象的实例,因为它们可能访问或修改对象的状态,而在静态上下文中没有this
实例引用可用来指向当前对象,因此无法确定要操作的具体对象实例。简而言之,静态方法无法确定非静态成员属于哪个对象的实例。
95、什么是方法的返回值?返回值的作用是什么?
方法的返回值是方法完成其操作后提供的输出。方法执行结束时,可以将一个值传回给调用者。返回值的数据类型在方法声明时指定,且方法必须返回声明类型的值(特殊情况是void类型,表示方法不返回任何值)。
返回值的作用是允许方法将结果传递回给调用者。这使得程序可以利用方法执行的结果进行进一步的操作或决策,增加了程序的模块性和复用性。例如,一个计算两个数和的方法会返回这两个数的和,调用者可以使用这个返回值进行其他操作。
96、内部类是什么?
内部类是定义在另一个类内部的类。它可以访问外部类的所有成员(包括私有成员),主要用于处理外部类中的某些特定问题,增强封装性。
97、内部类有哪些类型?
内部类主要有四种类型:
- 成员内部类:类似于类的成员变量,可以访问外部类的所有成员。
class OuterClass {class MemberInnerClass{}
}
- 静态内部类:使用static修饰的内部类,不能直接访问外部类的非静态成员。
class OuterClass {static class StaticInnerClass{}
}
- 局部内部类:在方法或作用域内部定义的类。
class OuterClass {void someMethod() {class LocalInnerClass {}}
}
- 匿名内部类:没有类名称,用于创建一次性使用的类实例。
new Thread(new Runnable() {@Overridepublic void run() {// code}
}).start();
98、内部类哪些优缺点?
使用内部类的优缺点包括:
优点:
- 封装性:内部类可以直接访问外部类的成员,包括私有成员,提高了封装性。
- 逻辑性:如果一个类只在另一个类的内部使用,将其作为内部类定义可以使代码组织更加逻辑性强。
- 增强可读性和维护性:将内部类放在使用它们的类的内部,可以使代码更加易于理解和维护。
- 实现多继承:通过内部类可以实现对多个类的继承,避免了Java单继承的限制。
缺点:
- 增加复杂性:内部类增加了代码结构的复杂性,对初学者来说可能不易理解。
- 编译生成多个
**.class**
文件:每个内部类都会编译生成一个单独的.class
文件,增加了部署应用时的复杂度。 - 可能导致内存泄漏:非静态内部类会隐式持有外部类的引用,如果不当使用可能导致内存泄漏问题。
99、内部类有哪些应用场景?
- 封装功能模块:当某个功能只在一个类内部使用时,使用内部类可以更好地封装这个功能。
- 实现接口而不暴露给外部:可以创建内部类来实现接口或继承某个类,而不使外部类的类型发生改变。
- 回调函数和事件处理:在图形用户界面(GUI)编程中,匿名内部类常用于事件监听器(listener)和回调函数,使代码更简洁。
- 迭代器模式:在实现迭代器模式时,可以使用内部类来实现
Iterator
接口,隐藏迭代逻辑。 - 减少代码冗余:如果多个类有相似的功能,可以通过内部类来共享这部分代码,减少冗余。
- 策略模式和工厂模式:在设计模式中,特别是策略模式和工厂模式,使用内部类可以提供更好的封装和数据隐藏。
100、局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final?
局部内部类和匿名内部类访问局部变量时,变量必须要加上final
(在Java 8及之后版本,即使不显式声明为final
,也要求局部变量事实上不被修改,称为"effectively final"),这是因为:
- 生命周期差异:当局部内部类或匿名内部类的对象存在时,方法中的局部变量可能已经退出作用域并被销毁。为了使内部类仍然可以访问这些变量,它们的值在创建内部类实例时被拷贝到内部类中。
- 数据一致性:如果局部变量被允许修改,那么内部类中拷贝的值和原始变量的值可能会不一致。将变量声明为
final
或确保其为"effectively final"可以避免这种不一致性,确保数据的稳定性和一致性。
101、构造器是否可被重写?
不能,构造器(constructor)不能被重写(override)。构造器是用于初始化一个新对象的特殊类型的方法,而重写涉及到两个方法间的多态性,这在父类和子类之间的方法中才会发生。不过,构造器可以被重载(overload),即在同一个类中可以有多个构造器,它们的参数列表不同。
102、HashSet如何检查重复?
HashSet
检查重复元素主要依赖于元素的hashCode()
方法和equals()
方法:
- 当向
HashSet
添加元素时,首先调用元素的hashCode()
方法计算其哈希码,以此确定元素存储在内部哈希表的哪个位置(即哪个桶)。 - 如果计算得到的哈希码在哈希表中没有对应的条目,则直接将元素存储在该位置,认为没有重复。
- 如果该哈希码对应的位置已经有元素存在(即发生哈希碰撞),则调用元素的
equals()
方法与该位置上的每个元素逐一比较。如果equals()
方法返回true
,则判断为重复元素,不会添加到HashSet
中;如果equals()
返回false
,则将新元素添加到该位置。
因此,HashSet
检查重复的效率高低直接受到其元素hashCode()
方法的实现和equals()
方法的实现质量的影响。
103、两个对象的hashCode()相同,则equals()也一定为true对吗?
不对。两个对象的hashCode()
相同,只意味着它们被存放在哈希表的同一个桶中,但并不意味着它们通过equals()
方法比较时一定为true
。hashCode()
相同是发生碰撞的情况,需要进一步通过equals()
方法来检查两个对象是否真正相等。
104、说说hashCode和equals方法的关系?
hashCode()
和 equals()
方法之间的关系体现在以下两个主要原则上:
- 一致性:如果两个对象通过
equals()
方法比较相等,则这两个对象的hashCode()
方法必须返回相同的整数值。 - 不一定反向成立:如果两个对象的
hashCode()
方法返回相同的值,它们通过equals()
方法比较不一定相等。
这种设计是为了确保对象可以正确地存储在基于哈希的集合中,如HashSet
、HashMap
等,保证集合的正确性和性能。
105、为什么重写equals时必须重写hashCode方法?
重写equals()
时必须重写hashCode()
方法,以维护hashCode()
与equals()
方法之间的一致性约定:如果两个对象相等(即equals()
方法返回true
),则它们的哈希码(hashCode()
方法返回的值)也必须相等。这个约定确保了基于哈希的集合(如HashSet
、HashMap
)能够正确地处理对象,维护集合的性能和正确性。如果不这样做,相等的对象可能具有不同的哈希码,导致无法在集合操作中正确识别对象,例如,可能会在HashSet
中添加重复元素,或在HashMap
中找不到键的正确映射。
106、说说你对hashCode()的了解?
hashCode()
是Java中 Object
类的一个方法,它返回对象的哈希码,即一个整数值。这个方法主要用于优化基于哈希表的集合(如HashMap
、HashSet
、HashTable
)的性能。在这些集合中,hashCode()
用于确定对象存储的位置(即哈希桶的索引),从而加快搜索、插入和删除操作的速度。每个对象的哈希码是根据对象的内部状态计算出来的,且在程序执行期间不应该改变。理想情况下,不同的对象应有不同的哈希码,但不同对象产生相同哈希码的情况(哈希冲突)也是允许的。
107、为什么要有hashCode?
hashCode
存在的原因是为了提高基于哈希表的数据结构(如HashMap
、HashSet
、HashTable
)的性能。通过将对象转换成哈希码(一个整数值),hashCode
允许快速定位对象应该存储的桶位置,从而实现快速插入、查找和删除操作。理想情况下,不同的对象会产生不同的哈希码,减少哈希冲突,确保数据结构的高效性。
108、对象相等和引用相等有什么不同?
对象的相等通常指的是两个对象的状态或内容相同,这通过重写equals()
方法来判断。而引用相等指的是两个引用变量指向内存中的同一个对象地址。简而言之,对象相等关注的是内容是否相同,而引用相等关注的是是否是同一个对象。
109、当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里是值传递还是引用传递?
Java中是值传递。当对象作为参数传递给方法时,传递的是对象引用的副本(值)。这意味着方法内部可以通过这个引用副本改变对象的属性,但不能改变外部引用本身指向的对象。
110、为什么Java中只有值传递?
Java中只有值传递是因为无论是基本数据类型还是对象,方法调用时传递的都是变量的副本。对于基本数据类型,这个副本是实际的值;对于对象,这个副本是对象引用的值(即内存地址的副本)。这样设计的目的是为了保护数据,避免原始数据被无意或恶意地修改,从而增强程序的安全性和可靠性。
111、值传递和引用传递有什么区别?
值传递(Pass by Value):方法调用时,实参向形参传递的是值的副本。对副本的任何修改不会影响到原始数据。
引用传递(Pass by Reference):方法调用时,实参向形参传递的是引用(或内存地址)的副本。通过这个引用的副本,方法可以修改原始对象所指向的数据。
区别在于,值传递不会影响原始数据,而引用传递允许方法修改原始对象。
112、JDK中常用的包有哪些?
- java.lang:包含语言基础类,如
String
、Math
、System
等。 - java.util:包含集合框架、日期时间类、事件模型、随机数生成等工具类。
- java.io:提供输入输出(I/O)相关的类和接口,用于读写数据。
- java.net:包含执行与网络相关的操作的类和接口,如URL处理、套接字编程。
- java.sql:提供了进行JDBC数据库编程的类和接口。
- java.awt:抽象窗口工具包,用于构建图形用户界面(GUI)和图像处理。
- javax.swing:提供了一套更加复杂的GUI组件库,建立在AWT之上。
- java.nio:新输入输出,提供了高速的、可伸缩的I/O操作。
113、java和javax有什么区别?
java
和 javax
包的区别主要在于它们的历史和用途上。java
包是Java的核心API的一部分,提供了最基础的类和接口,如集合框架、线程、异常处理等。javax
包最初被用来作为Java核心API的扩展,包含了额外的功能和工具,如Swing GUI工具包、XML处理、JavaMail等。随着时间的发展,javax
包中的一些API变得非常重要,但基本上,java
包含核心功能,而javax包含扩展功能或补充API。
114、java 中10流分为几种?
分为四种主要类型:字节输入流、字节输出流、字符输入流和字符输出流。
115、Files的常用方法都有哪些?
copy(Path source, Path target, CopyOption... options)
:复制文件或目录。move(Path source, Path target, CopyOption... options)
:移动或重命名文件或目录。delete(Path path)
:删除文件或目录。exists(Path path, LinkOption... options)
:检查文件或目录是否存在。size(Path path)
:返回文件的大小。createFile(Path path, FileAttribute<?>... attrs)
:创建一个新文件。createDirectory(Path path, FileAttribute<?>... attrs)
:创建一个目录。createDirectories(Path path, FileAttribute<?>... attrs)
:创建一个目录及其所有父目录。newBufferedReader(Path path)
:打开文件以进行读取。newBufferedWriter(Path path, OpenOption... options)
:打开或创建文件以进行写入。readAllLines(Path path)
:读取文件的所有行
到一个列表。
write(Path path, Iterable<? extends CharSequence> lines, OpenOption... options)
:将多行写入文件。isDirectory(Path path, LinkOption... options)
:检查是否为目录。isRegularFile(Path path, LinkOption... options)
:检查是否为普通文件。setLastModifiedTime(Path path, FileTime time)
:设置文件最后修改时间。
116、什么是反射机制?
Java中的反射机制是一种动态机制,允许程序在运行时访问、检测和修改它本身的类和对象的信息。它使得Java程序能够动态加载类、获取类的元数据(如类的方法、字段、构造器等)、调用对象的方法、修改对象的字段,即便在编译时这些类、方法、字段是未知的。反射主要通过java.lang.Class
类以及java.lang.reflect
包中的Method
、Field
、Constructor
等类实现。
117、Java反射机制有什么优缺点?
优点:
- 灵活性:允许程序在运行时动态加载和操作对象,提高了程序的灵活性和扩展性。
- 泛型擦除的解决:可以用于访问在编译时期被泛型擦除的类型信息。
- 框架开发:是许多Java框架(如 Spring,Hibernate)的基础,用于实现依赖注入、ORM等功能。
缺点:
- 性能开销:反射调用比直接代码调用慢,因为它需要进行类型检查和访问控制检查。
- 安全风险:允许运行时修改程序行为可能会引入安全漏洞。
- 破坏封装性:可以访问私有成员和方法,破坏了类的封装性。
- 代码复杂性:使代码更难理解和维护,尤其是对于非反射机制的使用者。
118、静态编译和动态编译的区别是什么?
区别主要在于编译时机和执行效率:
- 静态编译:在程序运行前,源代码被编译成机器语言代码。这种编译一次完成,生成的可执行文件直接在硬件上运行,通常提供更高的执行效率和更好的优化,但缺乏灵活性。
- 动态编译:在程序运行时,源代码或字节码被即时(JIT)编译成机器语言。这允许更高的灵活性和平台独立性,可以针对当前运行环境进行优化,但在编译过程中会增加额外的性能开销。
119、反射机制的应用场景有哪些?
- 框架开发:如Spring的依赖注入和Hibernate的ORM映射,通过反射自动装配对象和管理数据库操作。
- 插件化或模块化平台:动态加载和运行第三方开发的插件或模块。
- 单元测试框架:如JUnit使用反射来识别和执行测试方法。
- 动态代理:在运行时创建代理对象,用于拦截原始对象的方法调用,实现如AOP (面向切面编程)的功能。
- 配置解析:解析并应用配置文件中的设置,如通过反射根据配置实例化类和设置属性。
- 泛型类型擦除的补救:通过反射获取和操作泛型类型的实际参数类型。
120、Java获取反射的三种方法?
- 使用
**Class.forName()**
方法:传递类的全限定名(包括包名)作为字符串参数,适用于动态加载类。
Class<?> c = Class.forName("java.lang.String"):
- 通过
**.class**
语法:直接在类名后使用.class
获得对应的Class
对象,适用于编译时已知的类。
Class<?> c = String.class;
- 使用对象的
**.getClass()**
方法:对于任意对象,调用其.getClass()
方法获取其运行时类的Class
对象,适用于已有对象实例。
String s = "example";
Class<?> c = s.getClass();
121、字符型常量和字符串常量的区别是什么?
字符型常量(Character Constant)是单个字符,使用单引号(')括起来,如'A'
,在Java中占用2字节,表示一个单一的Unicode字符。
字符串常量(String Constant)是一系列字宇符的集合,使用双引号(")括起来,如"Hello"
,在Java中是String
类型的对象,可以包含零个或多个字符,占用的内存大小取决于字符串中字符的数量。
122、什么是字符串常量池?
字符串常量池(String Constant Pool)是Java堆内存的一部分,用于存储唯一的字符串常量。这种机制允许JVM节省内存空间,通过确保所有相同的字符串常量都指向内存中的同一个位置。当创建字符串字面量时(例如,通过直接赋值String s = "hello";
),JVM首先检查字符串常量池中是否存在相同内容的字符串。如果存在,就返回对该字符串的引用;如果不存在,就在池中创建一个新的字符串,并返回其引用。这种机制不适用于new String()
创建的字符串对象。
123、String 是最基本的数据类型吗?
不是,String
在Java中是一个类,属于引用数据类型,不是基本数据类型。Java中的基本数据类型包括 byte
、short
、int
、long
、float
、double
、boolean
和char
。
124、String有哪些特性?
- 不可变性:字符串一旦创建,其值就不能被改变。
- 常量池:字符串常量池帮助节省内存,使得相同内容的字符串共享内存。
- 线程安全:由于不可变性,字符串在Java中是线程安全的。
- 支持字符串拼接:可以使用
+
操作符进行字符串拼接,但每次拼接都会生成新的字符串对象。 - 支持
**intern()**
方法:可以确保字符串常量池中只有一个唯一的字符串实例。 - 实现了
**Serializable**
**和 ****Comparable**
接口:使得字符串可以序列化,并且可以自然地排序。
125、String为什么是不可变的吗?
主要因为以下几个原因:
- 安全性:字符串经常作为参数传递给网络连接和文件路径等敏感操作。不可变性保证了值不会被更改,确保安全性。
- 同步性:由于不可变,
String
可以在多线程环境下安全使用,无需额外的同步操作。 - 性能:不可变性使得字符串常量池的实现成为可能,相同的字符串宇面量可以共享相同的内存地址,节省内存。
- 哈希码缓存:由于
String
的内容不会改变,其哈希码可以被缓存,这在使用字符串作为HashMap
或HashSet
的键时可以提高性能。
126、String有什么办法可以变成可变的吗?
可以通过使用StringBuilder
或 StringBuffer
类使字符串变得可变。这两个类提供了用于修改字符串的API,如追加(append
)、插入(insert
)、删除( delete
)等操作。StringBuilder
通常用于单线程环境下,因为它不是线程安全的,但其性能比StringBuffer
更优,后者是线程安全的,适用于多线程环境。使用这些类可以构建和修改字符串,然后通过调用它们的toString()
方法将其转换回不可变的String
对象。
127、String 类可以被继承吗?
不可以,String
类在Java中被声明为final
,因此不能被继承。
128、String str="i"与 String str=new String(“i”)一样吗?
不一样。String str = "i";
创建的字符串会检查字符串常量池中是否存在内容为"i"的字符串,如果存在,则直接返回其引用;如果不存在,会在常量池中创建一个然后返回其引用。而String str = new String("i");
会在堆内存中创建一个新的String
对象,即使常量池中已存在内容为"i"的字符串,也会创建新的对象,不会使用常量池中的对象。
129、String s = new String(“xyz”);创建了几个字符串对象?
可能创建了两个字符串对象:一个在字符串常量池中(如果常量池中尚不存在字面量"xyz"),另一个是通过new String("xyz")
在堆上显式创建的。
130、如何将字符串反转?
可以使用StringBuilder
或StringBuffer
的reverse()
方法来反转字符串:
String original = "example";=;
String reversed = new StringBuilder(original).reverse().toString();
这段代码创建了一个StringBuilder
对象,初始化为原始字符串,然后调用reverse()
方法将其反转,最后通过toString()
方法将其转换回字符串。
131、数组有没有length()方法? String 有没有length()方法?
数组没有length()
方法,它有一个length
属性用来获取数组的长度。
String
有一个length()
方法用来获取字符串的长度。
132、String 类的常用方法都有那些?
length()
:返回字符串的长度。charAt(int index)
:返回指定索引处的字符。subString(int beginIndex, int endIndex)
:返回一个新字符串,它是此字符串的一个子字符串。concat(String str)
:将指定字符串连接到此字符串的末尾。indexOf(int ch)
,indexOf(String str)
:返回指定字符或字符串第一次出现的位置。lastIndexOf(int ch)
,lastIndexOf(String str)
:返回指定字符或字符串最后一次出现的位置。equals(0bject anObject)
:比较此字符串与指定对象。equalsIgnoreCase(String anotherString)
:与equals
方法类似,忽略大小写差异。startsWith(String prefix)
:测试此字符串是否以指定的前缀开始。endsWith(String suffix)
:测试此字符串是否以指定的后缀结束。tolowerCase()
:使用默认语言环境的规则将此String
中的所有字符都转换为小写。toUpperCase()
:使用默认语言环境的规则将此String
中的所有字符都转换为大写。trim()
:返回字符串的副本,忽略前导空白和尾部空白。replace(char oldChar, char newChar)
、replace(CharSequence target, CharSequence replacement)
:返回一个新的字符串,它是通过用新字符替换此字符串中出现的所有旧字符或子字符串得到的。split(String rege)
:根据给定正则表达式的匹配拆分此字符串。valueOf(各种类型)
:返回各种类型数据的字符串表示形式。
133、在使用 HashMap 的时候,用String 做 key有什么好处?
使用String
作为HashMap
的key
有以下好处:
- 不可变性:
String
的不可变性保证了key
的唯一性和一致性,确保了HashMap
中key
的哈希值不会改变。 - 哈希码缓存:
String
类内部缓存了其哈希码,当再次使用相同的String
作为key
查找时,可以快速访问,提高了查找效率。 - 天然的唯一性和等价性:
String
重写了equals()
和hashCode()
方法,保证了只要内容相同,无论是哪个String
实例,都能正确地映射到相同的值,这使得使用String
作为key
时,HashMap
的行为非常直观和可预测。
134、String为什么是不可变的?
- 安全性:字符串经常作为参数传递给网络连接和文件路径等敏感操作。不可变性保证了值不会被更改,确保安全性。
- 同步性:由于不可变,
String
可以在多线程环境下安全使用,无需额外的同步操作。 - 性能:不可变性使得字符串常量池的实现成为可能,相同的字符串字面量可以共享相同的内存地址,节省内存。
- 哈希码缓存:由于
String
的内容不会改变,其哈希码可以被缓存,这在使用字符串作为HashMap
或HashSet
的键时可以提高性能。
135、String和StringBuffer、 StringBuilder的区别是什么?
- String:不可变的字符序列,每次修改都会生成新的
String
对象,适用于少量的字符串操作。 - StringBuffer:可变的字符序列,线程安全,适用于多线程环境下的字符串操作,性能略低于
StringBuilder
由于同步开销。 - StringBuilder:可变的字符序列,非线程安全,适用于单线程环境下的字符串操作,性能高于
StringBuffer
因为省去了同步开销。
136、Integer a=127与Integer b=127相等吗?
是的,Integer a = 127
与Integer b = 127
相等。在Java中,整数值在-128
到127
之间的Integer
实例会被缓存,因此a
和b
引用的是同一个Integer
对象。
137、Integer a=128与Integer b=128相等吗?
不相等。在Java中,超过-128
到127
这个范围的整数不会被缓存,因此Integer a = 128
和 Integer b = 128
会指向不同的Integer
对象实例。使用==
比较时,比较的是引用,不是值。