Byte
。Short
。Integer
。Long
。Float
。Double
。Character
。Boolean
。instanceof
关键字用于检查一个对象是否是指定类或其子类的实例,返回true
或false
。
自动装箱:基本类型自动转换为对应的包装类型。例如:
int
转换为Integer
double
转换为Double
boolean
转换为Boolean
自动拆箱:包装类型自动转换为对应的基本类型。例如:
Integer
转换为int
Double
转换为double
Boolean
转换为boolean
重载(Overloading):
重写 (Overriding):
==
:比较两个对象的引用或基本数据类型的值是否相同。
equals
:默认比较对象的引用,可被重写来比较对象的内容。
hashCode()
方法的作用是返回对象的哈希码,主要用于优化信息存储结构,如哈希表(HashMap
、 HashSet
等),通过哈希码来快速定位对象的存储地址,提高数据访问效率。在Java中相等的对象必须有相同的哈希码。
HashMap
:非线程安全。HashTable
:线程安全,通过方法同步。HashMap
:因非线程安全,性能较高。HashTable
:线程安全导致性能较低。HashMap
:允许一键为null,多个值为null。HashTable
:键和值都不能为null。HashMap
:使用Iterator
遍历。HashTable
:使用Enumerator
或 Iterator
遍历。HashMap
:继承自AbstractMap
类。HashTable
:继承自Dictionary
类。Collection包结构
java.util.Collection
是Java集合框架的根接口,它下面有几个主要的子接口:
ArrayList
、LinkedList
)。HashSet
、TreeSet
)。LinkedList
、PriorityQueue
)。Collections的区别
sort
)、搜索(search
)等,它作用于 Collection 接口及其子接口的实现。总结:Collection
是一个集合接口,定义了集合的基本操作。Collections
是一个操作集合的工具类,提供了一系列静态方法来操作集合数据。
Object obj = new Object();
。只要强引用存在,垃圾回收器永远不会回收被引用的对象。SoftReference
类实现。软引用的对象在系统将要发生内存溢出之前,会将这些对象列入回收范围进行二次回收。适用于缓存实现。WeakReference
类实现。弱引用的对象只能生存到下一次垃圾收集发生之前。无论内存是否足够,都会被回收。PhantomReference
类实现。虚引用的对象完全不会影响其生命周期,仅仅提供了一种确保对象被 finalize后,能做某些事情的机制。new
关键字:最常见的方式。Class.forName()
配合newInstance()
:反射机制创建对象。clone()
方法:克隆一个对象。Constructor.newInstance()
:反射中的Constructor创建对象。是的,两个不相等的对象可以有相同的hashCode
。这种情况称为哈希碰撞(HashCollision)。由于哈希码的值域(通常是int
类型的范围)有限,而实际对象的数量可能远远超出这个范围,理论上不可能为每个不同的对象分配一个唯一的哈希码。因此不同对象产生相同哈希码是可能的。
浅拷贝:只复制对象及其基本数据类型的值,对象内的引用类型指向同一地址。
深拷贝:复制对象及其内部所有层级的对象,完全独立,无共享引用。
3 * 0.1 == 0.3
的返回值是False
。
这是因为浮点数的精度问题,计算机在处理浮点数时可能不会得到完全精确的结果。
a = a + b
需要a
和b
类型完全匹配,或能够通过类型提升自动匹配。a += b
内部包含隐式的类型转换,即如果a
和b
类型不匹配,b
的类型会被自动提升或转换为a
的类型,然后再进行赋值。a += b
更简洁。a += b
可能会略微高效,尽管这种差异通常非常小,对性能影响不大。是的,即使try
块中有return
语句,finally
块仍然会执行。finally
块设计用于确保无论try
块内发生什么(包括返回和抛出异常),都能执行清理或释放资源等操作。
Exception
和Error
都是Throwable
类的子类,它们构成了Java异常处理的两大分支。
OutOfMemoryError
、StackOverflowError
等。结构图如下:
Throwable
|——Exception
| |——IOException
| |——SQLException
| |——RuntimeException
| |——NullPointerException
| |——IndexOutOfBoundsException
| |——ArithmeticException
|——Error
|——LinkageError
|——VirtualMachineError
| |——OutOfMemoryError
| |——StackOverflowError
|——AssertionError
常见的运行时异常(RuntimeException
的子类)包括:
NullPointerException
:当尝试使用null
对象引用执行操作时抛出。ArrayIndexOutOfBoundsException
:尝试访问数组时使用了非法索引(负数或超出数组大小)。ArithmeticException
:发生异常的算术条件,例如除以零。ClassCastException
:尝试将对象强制转换为不是实例的类时抛出。IllegalArgumentException
:向方法传递了一个不合法或不适当的参数。IllegalStateException
:在不合法或不适当的时间调用方法。NumberFormatException
:尝试将字符串转换为数字格式,但字符串不具有适当的格式。IndexOutOfBoundsException
:某种类型的索引(如数组、字符串或向量)超出范围时抛出。ConcurrentModificationException
:在对集合进行迭代时,除了迭代器自身之外的其他方式修改了集合,就会抛出此异常。被检查的异常(Checked Exception)是那些在编译时必须被显式处理(捕获或通过方法签名声明抛出)的异常。它们直接继承自Exception
类,但不包括继承自RuntimeException
的异常。一些常见的被检查异常包括:
IOException
:输入输出操作失败或中断时抛出,如读写文件错误。FileNotFoundException
:尝试打开文件失败时抛出,文件不存在等情况。ClassNotFoundException
:找不到类定义时抛出。SQLException
:处理数据库时发生错误时抛出。MalformedURLException
:指定了未知协议或格式错误的 URL 时抛出。InterruptedExceptionv
:线程被另一个线程中断时抛出。NoSuchMethodException
:请求的方法不存在时抛出。SAXExceptionv
:处理XML文件错误时抛出。ParseException
:解析日期、时间等格式出错时抛出。OutOfMemoryError
: JVM没有足够的内存分配对象。StackOverflowError
:栈溢出,通常是由深度递归调用导致。NoClassDefFoundError
:找不到类定义,可能是因为类路径问题。UnsatisfiedLinkError
:当Java虚拟机不能找到一个本地方法的声明时抛出。NoSuchMethodError
:调用不存在的方法。ExceptionInInitializerError
:静态初始化器抛出异常或无法加载静态初始化块。LinkageError
:类依赖性问题,如类的不同版本之间的不兼容。ThreadDeath
:线程被stop()
方法杀死时抛出。VirtualMachineError
: JVM发生内部错误或资源不足时的超类。遇到的OutOfMemoryError
(OOM)情况:
ByteBuffer.allocateDirect()
分配的直接内存过多。-Xss
)导致总体占用空间超过可用内存。遇到的StackOverflowError
(SOF)情况:
面试官追问如何解决?通常这些情况通常需要通过代码优化、合理配置JVM参数、监控和分析堆栈/内存使用情况来解决。
程序:是指令和数据的集合,存储在磁盘上的文件,是静态的代码。
进程:程序的一次执行过程,是系统资源分配的基本单位。它拥有独立的内存空间,包含代码、数据以及执行的上下文。
线程:是进程中的执行单元,是CPU调度的基本单位。一个进程可以包含多个线程,它们共享进程的资源但能够并行执行任务。
关系:
简而言之程序是死的,进程是程序的活体表现,线程是进程的执行路径。一个程序可以被多个进程加载执行,一个进程可以包含多个线程共同完成任务,实现并发。
如果不希望某些字段被序列化,可以将这些字段声明为transient
。使用transient
关键字修饰的字段不会被序列化到输出流中,因此在反序列化时,这些字段将被忽略,其值会被设置为类型的默认值(例如,数值型为0,对象引用为null)。
IO流用于读取和写入数据,主要分为四大类,根据数据类型分为字节流和字符流,根据流向分为输入流和输出流:
InputStream
是所有字节输入流的父类,用于从源读取字节数据。OutputStream
是所有字节输出流的父类,用于向目的地写出字节数据。Reader
是所有字符输入流的父类,用于从源读取字符数据。Writer
是所有字符输出流的父类,用于向目的地写出字符数据。主要流类
FileInputStream
、FileOutputStream
、BufferedInputStream
、BufferedOutputStream
等。FileReader
、FileWriter
、BufferedReader
、BufferedWriter
等。特殊流类
InputStreamReader
和OutputStreamWriter
转换流可以将字节流和字符流进行转换。ObjectInputStream
和ObjectOutputStream
用于对象的序列化和反序列化。PrintStream
和PrintWriter
提供了打印各种数据值表示的便捷方法。使用场景
区别:
反射的作用
Java反射机制允许程序在运行时取得任何类的内部信息,并能操作任意对象的内部属性及方法。它的主要作用包括:
new
关键字实例化。反射的原理
Java反射是通过类加载机制来实现的。当Java程序运行时,Java虚拟机(JVM)会将使用到的类加载到内存中,并为每个类创建一个Class
类型的对象(即类对象),用于存储类的元数据信息(如类的名称、方法信息、字段信息等)。当程序通过反射API(如Class.forName()
,obj.getClass()
,.class
语法)获取到对应的Class
对象后,就可以利用这个对象来访问类的信息和动态操作类或对象。
Class
对象:加载过程中,JVM为每个类创建Class
对象,其中包含了类的元数据信息。Class
对象:程序可以通过Class
对象访问类的元数据,创建实例,访问字段和方法等。List:
ArrayList
,LinkedList
。Set:
HashSet
,LinkedHashSet
,TreeSet
。Map:
Collection
接口的一部分。HashMap
,LinkedHashMap
,TreeMap
。区别:
LinkedHashSet
),Map不保证键值对的顺序(除LinkedHashMap
)。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
:
.class
语法,如String.class
。getClass()
方法,如"hello".getClass()
。Class.forName()
静态方法,如Class.forName("java.lang.String")
。ArrayList
基于动态数组。LinkedList
基于双向链表。ArrayList
优于LinkedList
在随机访问数据。LinkedList
优于 ArrayList
在添加和删除操作。ArraylList
更紧凑,但在扩容时需要重新分配内存并复制。LinkedList
为每个元素使用更多内存(因节点引用)。ArrayList
可以自动扩展和缩减大小,提供了更大的灵活性。ArrayList
提供了大量方法来进行元素的插入、删除、遍历等操作,使用起来更加方便。ArrayList
支持泛型,可以在编译时检查元素类型,提高代码的安全性和可读性。ArrayList
是Java 集合框架的一部分,可以与其他集合类型(如Set
、Map
)无缝集成,提供了统一的编程接口。Fail-fast 是一种错误检测机制,指的是在操作集合(如列表、集合等)时,如果检测到集合在迭代过程中被结构性地修改(例如添加、删除元素),则立即抛出ConcurrentModificationException
异常。这种机制旨在快速失败,避免不确定的行为和潜在的错误。Java集合框架中的很多实现(如ArrayList
、HashMap
)都采用了fail-fast 机制。它主要通过在迭代器内部维护一个修改计数器(modCount)来实现,每次迭代前检查计数器是否有变化,以保证迭代的一致性和安全性。
Hashtable
是线程安全的,HashMap
不是。Hashtable
性能较HashMap
低。Hashtable
不允许键或值为null
,HashMap
允许一个键为null
和多个值为null
。Hashtable
继承自Dictionary
类,HashMap
是Java Collections Framework的一部分。是的,可以使用任何类作为HashMap
中的key,但为了确保HashMap
正确地存储和检索键值对,有两个重要条件需要满足:
equals()
方法:确保当两个键对象逻辑上等价时,可以返回true
。hashCode()
方法:确保逻辑上等价的键对象产生相同的哈希码。如果这两个方法没有被适当地重写,HashMap
可能无法准确地识别键对象,导致数据存取错误。
HashMap的长度是2的N次方主要是为了优化索引计算的效率。这样设计可以使得索引的计算通过位运算(hash & (length-1)
)来完成,这比模运算更高效。同时,这种长度能够帮助哈希表中的元素更均匀地分布,减少哈希冲突,提高了HashMap的整体性能。
相同点:
Map
接口,用于存储键值对。不同点:
HashMap
是非线程安全的,适用于单线程环境。ConcurrentHashMap
是线程安全的,适用于多线程环境。ConcurrentHashMap
在多线程环境下性能优于使用Collections.synchronizedMap(new HashMap<>())
包装的HashMap
。HashMap
允许一个null
键和多个null
值。ConcurrentHashMap
不允许null
键和null
值,因为并发读取时无法区分返回值null
是映射值本身为 null还是键不在映射中。HashMap
在扩容时直接对整个哈希表进行扩容。ConcurrentHashMap
使用分段锁(在Java 8以后是通过分散数组、链表和红黑树的结构,以及CAS操作和synchronized
来保证线程安全),减少了锁竞争,提高了效率。红黑树的五个特征:
Exception
或Throwable
。关系:JDK用于开发Java程序,包括JRE来运行编译后的程序,而JRE包含JVM来提供一个平台无关的运行环境。简而言之,JDK > JRE > JVM,其中JDK是包含JRE的,而JRE是包含JVM的。
在Java中,public
、protected
、default
(无修饰符)和private
关键字用于指定类成员(方法和变量)的访问级别:
这些修饰符从宽松到严格排序为:public > protected > default > private,它们定义了类成员在不同上下文中的可见性和访问性。
多态是面向对象编程中的一个核心概念,指的是同一操作作用于不同的对象时,可以有不同的解释和表现。在Java中,多态可以通过继承(inheritance)和接口(interfaces)来实现,具体体现为:
ArrayList
是Java中的一个可调整大小的数组实现,属于java.util
包。它允许存储任意类型的对象(包括null
),并且可以动态地增加和减少其容量。ArrayList
提供了快速的随机访问能力(即按索引访问元素),但在列表中间插入或删除元素的速度相对较慢,因为这可能需要移动现有元素。ArrayList
实现了List
接口,因此它支持所有的列表操作,如添加、删除、清空列表以及支持迭代器等。由于其内部是通过数组实现的,当元素被添加到ArrayList
中而数组容量不足时,其内部会自动扩容以容纳更多的元素。
LinkedList
是Java中的一个双向链表实现,属于java.util
包。它允许存储任意类型的对象,包括null
。与ArrayList
相比,LinkedList
提供了更高效的元素插入和删除操作,因为这些操作通常只需改变节点的指针,而不需要移动其他元素。然而,LinkedList
在随机访问元素时性能较低,因为它需要从头或尾开始遍历链表来访问特定索引的元素。
LinkedList
实现了List
接口,因此支持所有列表操作,如添加、删除和遍历元素。此外,LinkedList
还实现了Deque
接口,使其能够被用作双端队列进行元素的入队和出队操作。由于其链表的特性,LinkedList
在实现栈、队列或双端队列时是一个好的选择。
Vector
是Java中的一个动态数组实现,属于java.util
包。与ArrayList
类似,Vector
也支持动态扩容,可以根据需要增加和减少容量来存储任意类型的对象。不同之处在于Vector
是同步的(synchronized),这意味着它是线程安全的。因此,在多线程环境中,当多个线程同时访问Vector
实例时,它保证了数据的一致性和完整性。
由于其线程安全的特性,Vector
的性能可能会比非同步的ArrayList
稍低。Vector
提供了类似于ArrayList
的API,支持快速随机访问、元素的添加、删除和遍历等操作。然而,在新的Java应用中,通常推荐使用Collections.synchronizedList
或CopyOnWriteArrayList
来获得线程安全的列表,而不是直接使用Vector
。
Set
是Java中的一个接口,属于java.util
包,代表一个不包含重复元素的集合。它是Java集合框架(Java Collections Framework)的一部分,用于存储一组唯一的元素,不保证元素的顺序。Set
接口主要有以下几个实现:
HashSet
。Set
接口支持基本操作如添加、删除元素以及判断元素是否存在。由于Set
不允许重复元素,添加已存在的元素会被忽略。Set
常用于去除重复数据、集合运算(如并集、交集、差集)等场景。
Map
是Java中的一个接口,表示一个键(Key)到值(Value)的映射。它不能包含重复的键,每个键最多只能映射到一个值。这个接口主要用于存储键值对,键和值都可以是任意类型的对象,包括null
。Map
接口的实现类有多种,包括但不限于:
Comparator
进行排序。HashMap
类似,但是它是同步的,不允许键或值为null
。Map
提供的操作包括添加、删除键值对,检查键或值是否存在,以及访问键集、值集或键值对集。它是处理键值对数据的重要数据结构,广泛应用于需要快速查找数据的场景。
遍历Map
的方法主要有以下几种:
keySet()
遍历键集:先获取Map
的所有键的集合,然后通过遍历这些键来访问每个键对应的值。values()
遍历值集:直接获取Map
中所有值的集合,遍历这个集合可以访问所有的值,但无法直接获取对应的键。entrySet()
遍历键值对:获取Map
中所有键值对的集合,然后遍历这个集合。每个元素都是Map.Entry
对象,可以通过它获取键和对应的值。forEach
方法:利用forEach
方法和Lambda
表达式直接对键值对进行操作,更加简洁高效。lterator
)遍历:通过keySet()
、values()
或 entrySet()
获取迭代器,然后使用迭代器进行遍历。HashMap
是Java中基于哈希表实现的Map
接口的一个类。它存储键值对,并允许使用null
值和null
键。HashMap
不保证映射的顺序;随着时间的推移,这个顺序可能会改变。它不是线程安全的,如果多线程同时访问HashMap
且至少有一个线程修改了映射,则必须外部同步。HashMap
提供了常数时间的性能 (O(1)
)对于基本操作,如获取和插入元素,假设哈希函数将元素适当地分散在桶中。它通过使用哈希码为每个键值对分配一个桶来实现快速查找和插入操作。
TreeMap
是Java中基于红黑树实现的Map接口的一个类。它存储键值对,并且按照键的自然顺序(或者根据构造TreeMap
时提供的Comparator
所指定的顺序)对键进行排序,从而保证了元素的有序性。TreeMap
不允许使用null
键(如果使用自然排序),但允许使用null值。与HashMap
相比,TreeMap
提供了一致的O(log n)
时间性能对于包含。个元素的映射的查找、插入和删除操作,因为红黑树是一种自平衡的二叉查找树。TreeMap
适合于需要按顺序访问键的场景,如实现范围查找和排序操作。
.class
文件中。字节码是独立于机器的代码,需要通过Java虚拟机(JVM)来解释执行或编译执行。Java和C++主要区别包括:
switch
可以作用在byte
类型上。switch
不能作用在long
类型上。switch
可以作用在 String
类型上。Math.round(11.5)
等于12。Math.round(-11.5)
等于-11。不正确。字面量3.4默认是double
类型的,直接赋值给float
变量会导致编译错误。应该声明为float
类型,方法是在数字后加上f
或F
,如:
float f = 3.4f;
short s1 = 1; s1 = s1 + 1;
,有错误。因为1
是int
类型,s1+1
的结果也会被提升为int
类型,不能直接赋值给short
类型的变量s1
,除非进行强制类型转换。short s1 = 1; s1 += 1;
,没有错误。+=
操作符会自动处理类型转换的问题,它会将右侧表达式的结果转换为左侧变量的类型,然后再赋值,因此不会有编译错误。Java语言采用Unicode编码方案。特点包括:
char
类型用于表示一个16位的Unicode字符(UTF-16编码)。Java有三种注释方式:
//
,注释从//
开始到行末。/*
和*/
包围,可以跨越多行。/**
和*/
包围,用于生成JavaDoc文档。&
是位运算符,用于按位与操作;在布尔逻辑中,也可以作为逻辑与操作,但它会对两边的表达式都进行求值。
&&
是逻辑与运算符,仅用于布尔逻辑中。它具有短路特性,即如果第一个操作数为false
,则不计算第二个操作数。
final
关键字在Java中有三种主要用途:
this
关键字在Java中主要有以下用途:
this
用来引用当前对象的实例。this()
用来调用同一个类的其他构造器。this
作为参数传递给其他方法或构造器。this
来区分成员变量和局部变量,特别是当它们名称相同时。super
关键字在Java中主要有以下用途:
super
来访问父类中被子类覆盖(重写)的方法和变量。super()
用来调用父类的构造器。如果没有显式调用,Java编译器会自动插入对父类无参构造器的调用。super
来引用父类的成员。区别:
this
引用的是当前对象的实例,用于访问当前类的成员(变量、方法)、调用当前类的其他构造器、或者将当前对象传递给其他方法。super
引用的是当前对象的父类实例,用于访问父类的成员(变量、方法)和调用父类的构造器,特别是访问被子类覆盖的成员或调用父类的构造器。简而言之,this
用于指代当前类的上下文,而super
用于指代父类的上下文。
static
存在的主要意义在于:
static
关键字用于声明类级别的变量和方法,使得它们可以在没有创建类实例的情况下被访问。static
变量被类的所有实例共享。static
方法可以创建无需对象实例即可调用的工具方法,例如Math.sqrt(double)
。static
变量和方法属于类,而非类的实例,因此它们在内存中只有一份,有助于减少内存使用。static
初始化代码块,可以在类被加载时执行初始化操作。static
的独特之处在于:
static
成员(变量和方法)属于类本身,而非类的实例对象。static
变量,static
方法可以在没有类实例的情况下直接通过类名调用。static
变量和static
代码块在类加载到JVM时仅初始化一次。static
成员。static
方法常用作工具或帮助方法,如Math
类中的方法。static
关键字的应用场景包括:
static final
定义类级别的常量。static
。static
变量存储类的单一实例。使用static
时的注意事项包括:
static
变量存储在静态区,随类的加载而加载,随类的消失而消失,过多使用可能增加内存负担。static
变量如果被多个线程访问,需要考虑同步机制以避免线程安全问题。static
成员的生命周期长于任何对象实例,它们在程序启动时被初始化,程序结束时被销毁。static
方法内不能直接访问非静态成员。static
,应当仅在表示类级别的共享数据或行为时使用。for
、while
、do-while
)或者switch
语句。switch
语句,不会退出方法。void
方法)。return
不仅能结束循环,还能结束整个方法的执行,并将控制权交回方法被调用的地方。如果方法声明了返回类型,return
后应跟一个返回值。简而言之,break
用于完全结束循环或switch
语句,continue
用于结束当前迭代进入下一次循环,而return
用于结束整个方法的执行。
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
的外层循环。
面向对象编程(OOP)的主要特征包括:
多态机制是面向对象编程中的一个核心概念,允许不同类的对象对同一消息(方法调用)作出不同的响应。多态主要有两种形式:
Java中多态的实现主要依赖于以下三个机制:
面向对象编程的五大基本原则,简称为SOLID原则,包括:
抽象类和接口在Java中都用来定义抽象类型,它们有以下主要异同点:
相同点:
不同点:
public static final
的。public
的。抽象类用于表示"is-a"关系,用于类的继承;接口更多地用于表示"has-a"能力,或特定的行为契约。
区别如下:
super
关键字调用。不能,抽象类不能使用final
修饰。因为final
修饰的类不能被继承,而抽象类的目的是为了被其他类继承并实现其抽象方法。这两个修饰符的目的相互矛盾,所以不能同时使用。
创建对象使用new
关键字。例如,new ClassName()
会创建ClassName
类型的一个对象实例。
对象实例与对象引用的区别在于:
new
关键字创建的,它在内存中占有实际的空间,保存着对象的状态(属性值)和行为(方法)。简而言之,对象实例是具体的数据和方法的集合,而对象引用是一个指向那些数据和方法所在内存地址的变量。
区别主要包括:
主要作用是:
在调用子类构造方法之前先调用父类的无参数构造方法的目的是为了确保父类的状态被正确初始化。这一过程保证了在子类的构造方法执行之前,父类的成员变量和环境已经被设置好,确保继承体系中的对象在使用之前处于一个有效和一致的状态。
类的构造方法的作用是初始化对象,为对象成员变量设置初始值,并执行任何启动构造对象时必须的步骤。如果一个类没有声明构造方法,程序仍然能正确执行,因为Java编译器会为这个类自动提供一个默认的无参数构造方法(default constructor),这个构造方法没有参数,体内也没有具体执行语句。这保证了即使没有显式定义构造方法,也能实例化对象。
void
也不写。new
关键字创建类的新实例时,构造方法会自动被调用。static
、final
、abstract
、synchronized
修饰:构造方法不能被这些关键字修饰,因为它们与构造方法的目的不兼容。区别在于:
static
关键字声明,普通变量不使用。static
关键字声明,实例方法不使用。在一个静态方法内调用一个非静态成员是非法的,因为静态方法属于类本身,而不依赖于任何特定的对象实例来执行。非静态成员(包括变量和方法)需要依赖于对象的实例,因为它们可能访问或修改对象的状态,而在静态上下文中没有this
实例引用可用来指向当前对象,因此无法确定要操作的具体对象实例。简而言之,静态方法无法确定非静态成员属于哪个对象的实例。
方法的返回值是方法完成其操作后提供的输出。方法执行结束时,可以将一个值传回给调用者。返回值的数据类型在方法声明时指定,且方法必须返回声明类型的值(特殊情况是void类型,表示方法不返回任何值)。
返回值的作用是允许方法将结果传递回给调用者。这使得程序可以利用方法执行的结果进行进一步的操作或决策,增加了程序的模块性和复用性。例如,一个计算两个数和的方法会返回这两个数的和,调用者可以使用这个返回值进行其他操作。
内部类是定义在另一个类内部的类。它可以访问外部类的所有成员(包括私有成员),主要用于处理外部类中的某些特定问题,增强封装性。
内部类主要有四种类型:
class OuterClass {
class MemberInnerClass{
}
}
class OuterClass {
static class StaticInnerClass{
}
}
class OuterClass {
void someMethod() {
class LocalInnerClass {
}
}
}
new Thread(new Runnable() {
@Override
public void run() {
// code
}
}).start();
使用内部类的优缺点包括:
优点:
缺点:
class
文件:每个内部类都会编译生成一个单独的.class
文件,增加了部署应用时的复杂度。Iterator
接口,隐藏迭代逻辑。局部内部类和匿名内部类访问局部变量时,变量必须要加上final
(在Java 8及之后版本,即使不显式声明为final
,也要求局部变量事实上不被修改,称为"effectively final"),这是因为:
final
或确保其为"effectively final"可以避免这种不一致性,确保数据的稳定性和一致性。不能,构造器(constructor)不能被重写(override)。构造器是用于初始化一个新对象的特殊类型的方法,而重写涉及到两个方法间的多态性,这在父类和子类之间的方法中才会发生。不过,构造器可以被重载(overload),即在同一个类中可以有多个构造器,它们的参数列表不同。
HashSet
检查重复元素主要依赖于元素的hashCode()
方法和equals()
方法:
HashSet
添加元素时,首先调用元素的hashCode()
方法计算其哈希码,以此确定元素存储在内部哈希表的哪个位置(即哪个桶)。equals()
方法与该位置上的每个元素逐一比较。如果equals()
方法返回true
,则判断为重复元素,不会添加到HashSet
中;如果equals()
返回false
,则将新元素添加到该位置。因此,HashSet
检查重复的效率高低直接受到其元素hashCode()
方法的实现和equals()
方法的实现质量的影响。
不对。两个对象的hashCode()
相同,只意味着它们被存放在哈希表的同一个桶中,但并不意味着它们通过equals()
方法比较时一定为true
。hashCode()
相同是发生碰撞的情况,需要进一步通过equals()
方法来检查两个对象是否真正相等。
hashCode()
和 equals()
方法之间的关系体现在以下两个主要原则上:
equals()
方法比较相等,则这两个对象的hashCode()
方法必须返回相同的整数值。hashCode()
方法返回相同的值,它们通过equals()
方法比较不一定相等。这种设计是为了确保对象可以正确地存储在基于哈希的集合中,如HashSet
、HashMap
等,保证集合的正确性和性能。
重写equals()
时必须重写hashCode()
方法,以维护hashCode()
与equals()
方法之间的一致性约定:如果两个对象相等(即equals()
方法返回true
),则它们的哈希码(hashCode()
方法返回的值)也必须相等。这个约定确保了基于哈希的集合(如HashSet
、HashMap
)能够正确地处理对象,维护集合的性能和正确性。如果不这样做,相等的对象可能具有不同的哈希码,导致无法在集合操作中正确识别对象,例如,可能会在HashSet
中添加重复元素,或在HashMap
中找不到键的正确映射。
hashCode()
是Java中 Object
类的一个方法,它返回对象的哈希码,即一个整数值。这个方法主要用于优化基于哈希表的集合(如HashMap
、HashSet
、HashTable
)的性能。在这些集合中,hashCode()
用于确定对象存储的位置(即哈希桶的索引),从而加快搜索、插入和删除操作的速度。每个对象的哈希码是根据对象的内部状态计算出来的,且在程序执行期间不应该改变。理想情况下,不同的对象应有不同的哈希码,但不同对象产生相同哈希码的情况(哈希冲突)也是允许的。
hashCode
存在的原因是为了提高基于哈希表的数据结构(如HashMap
、HashSet
、HashTable
)的性能。通过将对象转换成哈希码(一个整数值),hashCode
允许快速定位对象应该存储的桶位置,从而实现快速插入、查找和删除操作。理想情况下,不同的对象会产生不同的哈希码,减少哈希冲突,确保数据结构的高效性。
对象的相等通常指的是两个对象的状态或内容相同,这通过重写equals()
方法来判断。而引用相等指的是两个引用变量指向内存中的同一个对象地址。简而言之,对象相等关注的是内容是否相同,而引用相等关注的是是否是同一个对象。
Java中是值传递。当对象作为参数传递给方法时,传递的是对象引用的副本(值)。这意味着方法内部可以通过这个引用副本改变对象的属性,但不能改变外部引用本身指向的对象。
Java中只有值传递是因为无论是基本数据类型还是对象,方法调用时传递的都是变量的副本。对于基本数据类型,这个副本是实际的值;对于对象,这个副本是对象引用的值(即内存地址的副本)。这样设计的目的是为了保护数据,避免原始数据被无意或恶意地修改,从而增强程序的安全性和可靠性。
值传递(Pass by Value):方法调用时,实参向形参传递的是值的副本。对副本的任何修改不会影响到原始数据。
引用传递(Pass by Reference):方法调用时,实参向形参传递的是引用(或内存地址)的副本。通过这个引用的副本,方法可以修改原始对象所指向的数据。
区别在于,值传递不会影响原始数据,而引用传递允许方法修改原始对象。
String
、Math
、 System
等。java
和 javax
包的区别主要在于它们的历史和用途上。java
包是Java的核心API的一部分,提供了最基础的类和接口,如集合框架、线程、异常处理等。javax
包最初被用来作为Java核心API的扩展,包含了额外的功能和工具,如Swing GUI工具包、XML处理、JavaMail等。随着时间的发展,javax
包中的一些API变得非常重要,但基本上,java
包含核心功能,而javax包含扩展功能或补充API。
分为四种主要类型:字节输入流、字节输出流、字符输入流和字符输出流。
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)
:创建一个目录及其所有父目录。new BufferedReader(Path path)
:打开文件以进行读取。new BufferedWriter(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)
:设置文件最后修改时间。Java中的反射机制是一种动态机制,允许程序在运行时访问、检测和修改它本身的类和对象的信息。它使得Java程序能够动态加载类、获取类的元数据(如类的方法、字段、构造器等)、调用对象的方法、修改对象的字段,即便在编译时这些类、方法、字段是未知的。反射主要通过java.lang.Class
类以及java.lang.reflect
包中的Method
、Field
、Constructor
等类实现。
优点:
缺点:
区别主要在于编译时机和执行效率:
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();
字符型常量(Character Constant)是单个字符,使用单引号(')括起来,如'A'
,在Java中占用2字节,表示一个单一的Unicode字符。
字符串常量(String Constant)是一系列字符的集合,使用双引号(")括起来,如"Hello"
,在Java中是String
类型的对象,可以包含零个或多个字符,占用的内存大小取决于字符串中字符的数量。
字符串常量池(String Constant Pool)是Java堆内存的一部分,用于存储唯一的字符串常量。这种机制允许JVM节省内存空间,通过确保所有相同的字符串常量都指向内存中的同一个位置。当创建字符串字面量时(例如,通过直接赋值String s = "hello";
),JVM首先检查字符串常量池中是否存在相同内容的字符串。如果存在,就返回对该字符串的引用;如果不存在,就在池中创建一个新的字符串,并返回其引用。这种机制不适用于new String()
创建的字符串对象。
不是,String
在Java中是一个类,属于引用数据类型,不是基本数据类型。Java中的基本数据类型包括 byte
、short
、int
、long
、float
、double
、boolean
和char
。
+
操作符进行字符串拼接,但每次拼接都会生成新的字符串对象。intern()
方法:可以确保字符串常量池中只有一个唯一的字符串实例。Serializable
和 Comparable
接口:使得字符串可以序列化,并且可以自然地排序。主要因为以下几个原因:
String
可以在多线程环境下安全使用,无需额外的同步操作。String
的内容不会改变,其哈希码可以被缓存,这在使用字符串作为HashMap
或HashSet
的键时可以提高性能。可以通过使用StringBuilder
或 StringBuffer
类使字符串变得可变。这两个类提供了用于修改字符串的API,如追加(append
)、插入(insert
)、删除( delete
)等操作。StringBuilder
通常用于单线程环境下,因为它不是线程安全的,但其性能比StringBuffer
更优,后者是线程安全的,适用于多线程环境。使用这些类可以构建和修改字符串,然后通过调用它们的toString()
方法将其转换回不可变的String
对象。
不可以,String
类在Java中被声明为final
,因此不能被继承。
不一样。String str = "i";
创建的字符串会检查字符串常量池中是否存在内容为"i"的字符串,如果存在,则直接返回其引用;如果不存在,会在常量池中创建一个然后返回其引用。而String str = new String("i");
会在堆内存中创建一个新的String
对象,即使常量池中已存在内容为"i"的字符串,也会创建新的对象,不会使用常量池中的对象。
可能创建了两个字符串对象:一个在字符串常量池中(如果常量池中尚不存在字面量"xyz"),另一个是通过new String("xyz")
在堆上显式创建的。
可以使用StringBuilder
或StringBuffer
的reverse()
方法来反转字符串:
String original = "example";=;
String reversed = new StringBuilder(original).reverse().toString();
这段代码创建了一个StringBuilder
对象,初始化为原始字符串,然后调用reverse()
方法将其反转,最后通过toString()
方法将其转换回字符串。
数组没有length()
方法,它有一个length
属性用来获取数组的长度。
String
有一个length()
方法用来获取字符串的长度。
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(各种类型)
:返回各种类型数据的字符串表示形式。使用String
作为HashMap
的key
有以下好处:
String
的不可变性保证了key
的唯一性和一致性,确保了HashMap
中key
的哈希值不会改变。String
类内部缓存了其哈希码,当再次使用相同的String
作为key
查找时,可以快速访问,提高了查找效率。String
重写了equals()
和 hashCode()
方法,保证了只要内容相同,无论是哪个String
实例,都能正确地映射到相同的值,这使得使用 String
作为key
时,HashMap
的行为非常直观和可预测。String
可以在多线程环境下安全使用,无需额外的同步操作。String
的内容不会改变,其哈希码可以被缓存,这在使用字符串作为HashMap
或HashSet
的键时可以提高性能。String
对象,适用于少量的字符串操作。StringBuilder
由于同步开销。StringBuffer
因为省去了同步开销。是的,Integer a = 127
与Integer b = 127
相等。在Java中,整数值在-128
到127
之间的Integer
实例会被缓存,因此a
和b
引用的是同一个Integer
对象。
不相等。在Java中,超过-128
到127
这个范围的整数不会被缓存,因此Integer a = 128
和 Integer b = 128
会指向不同的Integer
对象实例。使用==
比较时,比较的是引用,不是值。