赞
踩
不积跬步无以至千里,下面的内容是对网上原有的Java面试题集及答案进行了全面修订之后给出的负责任的题目和答案,原来的题目中有很多重复题目和无价值的题目,还有不少的参考答案也是错误的,修改后的Java面试题集参照了JDK最新版本,去掉了EJB 2.x等无用内容,补充了数据结构和算法相关的题目、经典面试编程题、大型网站技术架构、操作系统、数据库、软件测试、设计模式、UML等内容,同时还对很多知识点进行了深入的剖析,例如hashCode方法的设计、垃圾收集的堆和代、Java新的并发编程、NIO.2等,相信对准备入职的Java程序员一定有所裨益。
这里会不断收集和更新Java基础相关的面试题,目前已收集210题。
JAVA SE:主要用在客户端开发
JAVA EE:主要用在web应用程序开发
JAVA ME:主要用在嵌入式应用程序开发
JVM:java虚拟机,运用硬件或软件手段实现的虚拟的计算机,Java虚拟机包括:寄存器,堆栈,处理器
大多情况下是不需要的。Java提供了一个系统级的线程来跟踪内存分配,不再使用的内存区将会自动回收
JDK:java development kit:java开发工具包,是开发人员所需要安装的环境
JRE:java runtime environment:java运行环境,java程序运行所需要安装的环境
计算机保存,组织数据的方式
线性表(ArrayList)
链表(LinkedList)
栈(Stack)
队列(Queue)
图(Map)
树(Tree)
面向对象编程
世间万物都可以看成一个对象。每个物体包括动态的行为和静态的属性,这些就构成了一个对象。
类是对象的抽象,对象是类的具体,类是对象的模板,对象是类的实例
整形:byte,short,int,long
浮点型:float,double
字符型:char
布尔型:boolean
显示转换就是类型强转,把一个大类型的数据强制赋值给小类型的数据;隐式转换就是大范围的变量能够接受小范围的数据;隐式转换和显式转换其实就是自动类型转换和强制类型转换。
Char在java中也是比较特殊的类型,它的int值从1开始,一共有2的16次方个数据;Char<int<long<float<double;Char类型可以隐式转成int,double类型,但是不能隐式转换成string;如果char类型转成byte,short类型的时候,需要强转。
拆箱:把包装类型转成基本数据类型
装箱:把基本数据类型转成包装类型
byte:Byte short:Short int:Integer long:Long float:Float double:Double char:Character boolean:Boolean
属性、方法、内部类、构造方法、代码块。
不好,因为计算机在浮点型数据运算的时候,会有误差,尽量在布尔表达式中不使用浮点型数据(if,while,switch中判断条件不使用浮点型)
使用Bigdecimal类进行浮点型数据的运算
++i:先赋值,后计算
i++:先计算,后赋值
顺序结构
选择结构
循环结构
静态实例化:创建数组的时候已经指定数组中的元素,
int\[\] a=new int\[\]{1,3,3}
动态实例化:实例化数组的时候,只指定了数组程度,数组中所有元素都是数组类型的默认值
Byte,short,int,long默认是都是0
Boolean默认值是false
Char类型的默认值是’’
Float与double类型的默认是0.0
对象类型的默认值是null
Java.lang
http://Java.io
Java.sql
Java.util
Java.awt
http://Java.net
Java.math
Object
Equals
Hashcode
toString
wait
notify
clone
getClass
有指针,但是隐藏了,开发人员无法直接操作指针,由jvm来操作指针
理论上说,java都是引用传递,对于基本数据类型,传递是值的副本,而不是值本身。对于对象类型,传递是对象的引用,当在一个方法操作操作参数的时候,其实操作的是引用所指向的对象。
改变了,因为传递是对象的引用,操作的是引用所指向的对象
不能,数组一旦实例化,它的长度就是固定的
创建一个新数组,从后到前循环遍历每个元素,将取出的元素依次顺序放入新数组中
形参:全称为“形式参数”,是在定义方法名和方法体的时候使用的参数,用于接收调用该方法时传入的实际值;实参:全称为“实际参数”,是在调用方法时传递给该方法的实际值。
不能构造方法当成普通方法调用,只有在创建对象的时候它才会被系统调用
可以重写,也可以重载
方法的重载就是在同一个类中允许同时存在一个以上的同名方法,只要它们的参数个数或者类型不同即可。在这种情况下,该方法就叫被重载了,这个过程称为方法的重载(override)
静态内部类相对与外部类是独立存在的,在静态内部类中无法直接访问外部类中变量、方法。如果要访问的话,必须要new一个外部类的对象,使用new出来的对象来访问。但是可以直接访问静态的变量、调用静态的方法;
普通内部类作为外部类一个成员而存在,在普通内部类中可以直接访问外部类属性,调用外部类的方法。
如果外部类要访问内部类的属性或者调用内部类的方法,必须要创建一个内部类的对象,使用该对象访问属性或者调用方法。
如果其他的类要访问普通内部类的属性或者调用普通内部类的方法,必须要在外部类中创建一个普通内部类的对象作为一个属性,外同类可以通过该属性调用普通内部类的方法或者访问普通内部类的属性
如果其他的类要访问静态内部类的属性或者调用静态内部类的方法,直接创建一个静态内部类对象即可。
Static可以修饰内部类、方法、变量、代码块
Static修饰的类是静态内部类
Static修饰的方法是静态方法,表示该方法属于当前类的,而不属于某个对象的,静态方法也不能被重写,可以直接使用类名来调用。在static方法中不能使用this或者super关键字。
Static修饰变量是静态变量或者叫类变量,静态变量被所有实例所共享,不会依赖于对象。静态变量在内存中只有一份拷贝,在JVM加载类的时候,只为静态分配一次内存。
Static修饰的代码块叫静态代码块,通常用来做程序优化的。静态代码块中的代码在整个类加载的时候只会执行一次。静态代码块可以有多个,如果有多个,按照先后顺序依次执行。
- Final可以修饰类,修饰方法,修饰变量。
- 修饰的类叫最终类。该类不能被继承。
- 修饰的方法不能被重写。
- 修饰的变量叫常量,常量必须初始化,一旦初始化后,常量的值不能发生改变。
String,StringBuffer,StringBuilder
StringBuffer与StringBuilder都继承了AbstractStringBulder类,而AbtractStringBuilder又实现了CharSequence接口,两个类都是用来进行字符串操作的。
在做字符串拼接修改删除替换时,效率比string更高。
StringBuffer是线程安全的,Stringbuilder是非线程安全的。所以Stringbuilder比stringbuffer效率更高,StringBuffer的方法大多都加了synchronized关键字
- 不一样的。因为内存分配的方式不一样。
- 第一种,创建的”aaa”是常量,jvm都将其分配在常量池中。
- 第二种创建的是一个对象,jvm将其值分配在堆内存中。
一共有两个引用,三个对象。因为”aa”与”bb”都是常量,常量的值不能改变,当执行字符串拼接时候,会创建一个新的常量是” aabbb”,有将其存到常量池中。
Pow():幂运算
Sqrt():平方根
Round():四舍五入
Abs():求绝对值
Random():生成一个0-1的随机数,包括0不包括1
charAt:返回指定索引处的字符
indexOf():返回指定字符的索引
replace():字符串替换
trim():去除字符串两端空白
split():分割字符串,返回一个分割后的字符串数组
getBytes():返回字符串的byte类型数组
length():返回字符串长度
toLowerCase():将字符串转成小写字母
toUpperCase():将字符串转成大写字符
substring():截取字符串
format():格式化字符串
equals():字符串比较
不能。Equlas大多用来做字符串比较,要判断基本数据类型或者对象类型,需要使用==
\==可以判断基本数据类型值是否相等,也可以判断两个对象指向的内存地址是否相同,也就是说判断两个对象是否是同一个对象,Equlas通常用来做字符串比较。
Stringbuilder或者stringbuffer的reverse方法
封装、继承、多态
Java中既有单继承,又有多继承。对于java类来说只能有一个父类,对于接口来说可以同时继承多个接口
重载和重写都是java多态的表现。
重载叫override,在同一个类中多态的表现。当一个类中出现了多个相同名称的方法,但参数个数和参数类型不同,方法重载与返回值无关
重写叫overwrite,是字符类中多态的表现。当子类出现与父类相同的方法,那么这就是方法重写。方法重写时,子类的返回值必须与父类的一致。如果父类方法抛出一个异常,子类重写的方法抛出的异常类型不能小于父类抛出的异常类型。
可以重载,必须重写
必须重写
会执行。当创建一个子类对象,调用子类构造方法的时候,子类构造方法会默认调用父类的构造方法。
是java多态一种特殊的表现形式。创建父类引用,让该引用指向一个子类的对象
子类重写了父类方法和属性,访问的是父类的属性,调用的是子类的方法
- Super表示当前类的父类对象
- This表示当前类的对象
Abstract
不是必须。抽象类可以没有抽象方法。
包含抽象方法的类一定是抽象类
不可以。定义抽象类就是让其他继承的,而final修饰类表示该类不能被继承,与抽象类的理念违背了
- 普通类不能包含抽象方法,抽象类可以包含抽象方法
- 抽象类不能直接实例化,普通类可以直接实例化
接口就是某个事物对外提供的一些功能的声明,是一种特殊的java类
接口弥补了java单继承的缺点
接口中声明全是public static final修饰的常量
接口中所有方法都是抽象方法
接口是没有构造方法的
接口也不能直接实例化
接口可以多继承
抽象类有构造方法,接口没有构造方法
抽象类只能单继承,接口可以多继承
抽象类可以有普通方法,接口中的所有方法都是抽象方法
接口的属性都是public static final修饰的,而抽象的不是
编译时异常
运行时异常
ArrayIndexOutOfBoundsException:数组下标越界
异常捕捉:try…catch…finally,异常抛出:throws。
继承一个异常类,通常是RumtimeException或者Exception
会执行,如果有finally,在finally之后被执行,如果没有finally,在catch之后被执行
Try块必须存在,catch和finally可以不存在,但不能同时不存在
Throw写在代码块内,throw后面跟的是一个具体的异常实例 Throw写在方法前面后面,throws后面跟的是异常类,异常类可以出现多个
Error和Exception都是java错误处理机制的一部分,都继承了Throwable类。
Exception表示的异常,异常可以通过程序来捕捉,或者优化程序来避免。
Error表示的是系统错误,不能通过程序来进行错误处理。
有,log4j是用来日志记录的,记录一些关键敏感的信息,通常会将日志记录到本地文件或者数据库中。记录在本地文件中,会有频繁的io操作,会耗费一些系统资源。记录在数据库中,会频繁地操作数据库表,对系统性能也有一定的影响。但是为了程序安全以及数据的恢复或者bug的跟踪,这点资源消耗是可以承受的。
由低到高:debug、info、wran、error
Java反射
通过new创建对象的效率比较高。通过反射时,先找查找类资源,使用类加载器创建,过程比较繁琐,所以效率较低
Coillection、Map。
List:线性表、Set:无序集合。
顺序存储、可以有重复值。
无须存储、不能有重复值。
- ArrayList与LinkedList都实现了List接口。
- ArrayList是线性表,底层是使用数组实现的,它在尾端插入和访问数据时效率较高,
- Linked是双向链表,他在中间插入或者头部插入时效率较高,在访问数据时效率较低
Array与ArrayList都是用来存储数据的集合。ArrayList底层是使用数组实现的,但是arrayList对数组进行了封装和功能扩展,拥有许多原生数组没有的一些功能。我们可以理解成ArrayList是Array的一个升级版。
- 以键值对存储数据
- 元素存储循序是无须的
- 不允许出现重复键
加载数据库驱动类
打开数据库连接
执行sql语句
处理返回结果
关闭资源
使用PreparedStatement类,而不是使用Statement类
使用CallableStatement
数据库连接是非常消耗资源的,影响到程序的性能指标。连接池是用来分配、管理、释放数据库连接的,可以使应用程序重复使用同一个数据库连接,而不是每次都创建一个新的数据库连接。通过释放空闲时间较长的数据库连接避免数据库因为创建太多的连接而造成的连接遗漏问题,提高了程序性能。
Dbcp,c3p0等,用的最多还是c3p0,因为c3p0比dbcp更加稳定,安全;通过配置文件的形式来维护数据库信息,而不是通过硬编码。当连接的数据库信息发生改变时,不需要再更改程序代码就实现了数据库信息的更新。
按功能来分
输入流(input),输出流(output)
按类型来分
字节流,字符流
File
FileInputSteam,FileOutputStream
BufferInputStream,BufferedOutputSream
PrintWrite
FileReader,FileWriter
BufferReader,BufferedWriter
ObjectInputStream,ObjectOutputSream
- 以字节为单位输入输出数据,字节流按照8位传输
- 以字符为单位输入输出数据,字符流按照16位传输
抽象类:
接口:
进程是系统进行资源分配和调度的一个独立单位,线程是CPU调度和分派的基本单位
进程和线程的关系:
线程与进程的区别:
&是位运算符。&&是布尔逻辑运算符,在进行逻辑判断时用&处理的前面为false后面的内容仍需处理,用&&处理的前面为false不再处理后面的内容。
不会,在下一个垃圾回收周期中,这个对象将是可被回收的。
吞吐量收集器使用并行版本的新生代垃圾收集器,它用于中等规模和大规模数据的应用程序。而串行收集器对大多数的小应用(在现代处理器上需要大概100M左右的内存)就足够了。
JVM:Java虚拟机(JVM)是运行 Java 字节码的虚拟机。JVM有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。
什么是字节码?采用字节码的好处是什么?
在 Java 中,JVM可以理解的代码就叫做字节码(即扩展名为 .class 的文件),它不面向任何特定的处理器,只面向虚拟机。Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以 Java 程序运行时比较高效,而且,由于字节码并不针对一种特定的机器,因此,Java程序无须重新编译便可在多种不同操作系统的计算机上运行。
Java 程序从源代码到运行一般有下面3步:
我们需要格外注意的是 .class->机器码 这一步。在这一步 JVM 类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。而且,有些方法和代码块是经常需要被调用的(也就是所谓的热点代码),所以后面引进了 JIT 编译器,而JIT 属于运行时编译。当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。而我们知道,机器码的运行效率肯定是高于 Java 解释器的。这也解释了我们为什么经常会说 Java 是编译与解释共存的语言。
JDK:(Java Development Kit)Java开发工具包,它拥有JRE所拥有的一切,还有编译器(javac)和工具(如javadoc和jdb)。它能够创建和编译程序。
JRE:(Java Runtime Environment)Java运行时环境,它是运行已编译 Java 程序所需的所有内容的集合,包括 Java虚拟机(JVM),Java类库,java命令和其他的一些基础构件。但是,它不能用于创建新程序。
一个字节(Byte)占8位(8bit)
byte:1字节 short:2字节
int:4字节 long:8字节
float:4字节精确到7位有效数字 double:8字节
char:2字节 boolean:1位
引用类型:4字节 ,1个字节表示8位
父类的私有属性和构造方法并不能被继承,所以 Constructor 也就不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。
问:Java 构造方法能否被重写和重载?
重写是子类方法重写父类的方法,重写的方法名不变,而类的构造方法名必须与类名一致,假设父类的构造方法如果能够被子类重写则子类类名必须与父类类名一致才行,所以 Java 的构造方法是不能被重写的。而重载是针对同一个的,所以构造方法可以被重载。
封装 继承 多态
封装:
封装就是把抽象的数据和对数据进行的操作封装在一起,数据被保存在内部,程序的其他部分只有通过被授权的操作(成员方法)才能对数据进行操作。
java提供了四种控制修饰符控制方法和变量访问的权限:
继承是使用已存在的类的定义作为基础建立新类的技术。继承可以解决代码复用问题,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extend语句来声明继承父类。
关于继承如下 3 点请记住:
多态:
所谓多态,就是指一个引用(类型)在不同情况下的多种状态,你也可以这样理解:父类型的引用指向子类型的对象。
多态有两个好处:
1. 应用程序不必为每一个派生类编写功能调用,只需要对抽象基类进行处理即可。大大提高程序的可复用性。//继承
2. 派生类的功能可以被基类的方法或引用变量所调用,这叫向后兼容,可以提高可扩充性和可维护性。//多态的真正作用,
一、区别
1、String是字符串常量,而StringBuffer和StringBuilder是字符串变量。由String创建的字符内容是不可改变的,而由StringBuffer和StringBuidler创建的字符内容是可以改变的。
2、StringBuffer是线程安全的,而StringBuilder是非线程安全的。StringBuilder是从JDK 5开始,为StringBuffer类补充的一个单线程的等价类。我们在使用时应优先考虑使用StringBuilder,因为它支持StringBuffer的所有操作,但是因为它不执行同步,不会有线程安全带来额外的系统消耗,所以速度更快。
二、String为什么是不可变的?
String 类中使用 final 关键字修饰字符数组来保存字符串,private final char value[],所以 String 对象是不可变的。而StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。
String是被声明为final class,除了hash这个属性其它属性都声明为final。因为它的不可变性,所以例如拼接字符串时候会产生很多无用的中间对象,如果频繁的进行这样的操作对性能有所影响。
StringBuffer就是为了解决大量拼接字符串时产生很多中间对象问题而提供的一个类,提供append和add方法,可以将字符串添加到已有序列的末尾或指定位置,它的本质是一个线程安全的可修改的字符序列,把所有修改数据的方法都加上了synchronized。但是保证了线程安全是需要性能的代价的。
在很多情况下我们的字符串拼接操作不需要线程安全,这时候StringBuilder登场了,StringBuilder是JDK1.5发布的,它和StringBuffer本质上没什么区别,就是去掉了保证线程安全的那部分,减少了开销。
StringBuffer 和 StringBuilder 二者都继承了 AbstractStringBuilder ,底层都是利用可修改的char数组(JDK 9 以后是 byte数组)。
再说说字符串常量池:
Java为了避免在一个系统中产生大量的String对象,引入了字符串常量池。
创建一个字符串时,首先会检查池中是否有值相同的字符串对象,如果有就直接返回引用,不会创建字符串对象;如果没有则新建字符串对象,返回对象引用,并且将新创建的对象放入池中。但是,通过new方法创建的String对象是不检查字符串常量池的,而是直接在堆中创建新对象,也不会把对象放入池中。上述原则只适用于直接给String对象引用赋值的情况。
String str1 = new String("a"); //不检查字符串常量池的
String str2 = "bb"; //检查字符串常量池的
String还提供了intern()方法。调用该方法时,如果字符串常量池中包括了一个等于此String对象的字符串(由equals方法确定),则返回池中的字符串的引用。否则,将此String对象添加到池中,并且返回此池中对象的引用。
在JDK6中,不推荐大量使用intern方法,因为这个版本字符串缓存在永久代中,这个空间是有限了,除了FullGC之外不会被清楚,所以大量的缓存在这容易OutOfMemoryError。
之后的版本把字符串放入了堆中,避免了永久代被挤满。
参考:
https://baijiahao.baidu.com/s?id=1629804867201303563&wfr=spider&for=pc
对于三者使用的总结:
//自动装箱 Integer total = 99; //系统自动执行了Integer total = Integer.valueOf(99); //自定拆箱 int totalprim = total; //系统自动执行了int totalprim = total.intValue();
1. int是基本数据类型,Integer是int的包装类就是将int类型包装成Object对象;
2. Integer变量必须实例化后才能使用;int变量不需要;
3. Integer实际是对象的引用,指向此new的Integer对象;int是直接存储数据值 ;
4. Integer的默认值是null;int的默认值是0。
深入:
Integer.valueOf函数源码:
public static Integer valueOf(int i) {
return i >= 128 || i < -128 ? new Integer(i) : SMALL\_VALUES;i+128
}
它会首先判断i的大小:如果i小于-128或者大于等于128,就创建一个Integer对象,否则执行SMALL_VALUES[i + 128]。
SMALL_VALUES[i + 128]是什么东西:
private static final IntegerSMALL\_VALUES = new Integer;256
SMALL_VALUES本来已经被创建好,也就是说在i >= 128 || i < -128是会创建不同的对象,在i < 128 && i >= -128会根据i的值返回已经创建好的指定的对象。
对象封装有很多好处,可以把属性也就是数据跟处理这些数据的方法结合在一起,比如Integer就有parseInt()等方法来专门处理int型相关的数据。
另一个非常重要的原因就是在Java中绝大部分方法或类都是用来处理类类型对象的,如ArrayList集合类就只能以类作为他的存储对象,而这时如果想把一个int型的数据存入list是不可能的,必须把它包装成类,也就是Integer才能被List所接受。所以Integer的存在是很必要的。
非静态的方法可以调用静态的方法,但是静态的方法不可以调用非静态的方法。
类的静态成员(变量和方法)属于类本身,在类加载的时候就会分配内存,可以通过类名直接去访问;非静态成员(变量和方法)属于类的对象,所以只有在类的对象产生(创建类的实例)时才会分配内存,然后通过类的对象(实例)去访问。
在一个类的静态成员中去访问其非静态成员之所以会出错是因为在类的非静态成员不存在的时候类的静态成员就已经存在了,访问一个内存中不存在的东西当然会出错。
参考为什么不能从静态的方法里调用非静态的方法或变量_l18649805795的博客-CSDN博客
问题:static修饰变量、代码块时什么时候执行?执行几次?
在类加载的init阶段,类的类构造器中会收集所有的static块和字段并执行;static块只执行一次。
【注】:
Java 程序在执行子类的构造方法之前,如果没有用super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super()来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。
问题:子类继承父类时,父类的构造方法什么时候调用?
实例化一个子类对象时会先执行其父类的构造函数,然后再执行子类的构造函数。
super()必须先被调用;如果子类构造方法里没有写super(),编译器会自动调用super()方法,即调用父类的默认无参构造方法。所以不可以父类中只定义了有参数的构造方法(在Java中,如果一个类没有定义构造方法,编译器会默认插入一个无参数的构造方法;但是如果一个构造方法在父类中已定义,在这种情况,编译器是不会自动插入一个默认的无参构造方法)
接口和抽象类的相同点:
1)\==比较的是值是否相等
**如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;**
如果作用于引用类型的变量,则比较的是所指向的对象的地址。
2)equals方法不能作用于基本数据类型的变量,只能用于类变量。(对于基本数据类型要用其包装类)
如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;
诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。
【注】Object类中的equals方法和“==”是一样的,没有区别,而String类,Integer类等等一些类,是重写了equals方法,才使得equals和“==不同”,所以,当自己创建类时,自动继承了Object的equals方法,要想实现不同的等于比较,必须重写equals方法。
String a = new String("ab"); // a为一个引用 String b = new String("ab"); // b为另一个引用,对象的内容一样 String aa = "ab"; //放在常量池中 String bb = "ab"; //从常量池中查找 System.out.println(aa\==bb);//true System.out.println(a\==b); //false,非同一对象 System.out.println((a.equals(b)); //true System.out.println((42 == 42.0); //true
当对象的equals()方法被重写时,通常有必要重写 hashCode() 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
- (1)两个对象相等,hashcode一定相等
-
- (2)两个对象不等,hashcode不一定不等
-
- (3)hashcode相等,两个对象不一定相等
-
- (4)hashcode不等,两个对象一定不等
-
- hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。这样**如果我们对一个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,但不重写hashcode,那么我们再new一个新的对象,当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致**。
https://www.cnblogs.com/wang-meng/p/7501378.html
HashMap中的get与put:
(1)put:
1.首先根据put元素的key获取hashcode,然后根据hashcode算出数组的下标位置,如果下标位置没有元素,直接放入元素即可。
2.如果该下标位置有元素,则需要已有元素和put元素的key对象比较equals方法,如果equals不一样,则说明可以放入进map中,会在该数组位置创建一个链表,后put进入的元素到放链表头,原来的元素向后移动。
(2)get:
根据元素的key获取hashcode,然后根据hashcode获取数组下标位置,如果只有一个元素则直接取出。如果该位置是一个链表,则需要调用equals方法遍历链表中的所有元素与当前的元素比较,得到真正想要的对象。(当两个对象的hashcode不同的话,肯定他们不能equals。)阿里2021最全新的java面试题总结
(1) final 是一个修饰符,
注:
final关键字主要用在三个地方:变量、方法、类。
对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
当用final修饰一个类时,表明这个类不能被继承。final类中的所有成员方法都会被隐式地指定为final方法。
使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的Java版本已经不需要使用final方法进行这些优化了)。类中所有的private方法都隐式地指定为final。
(2)finally:用来在异常处理中,如果抛出一个异常,则相匹配的 catch 子句就会执行,然后控制就会进入 finally 块
finally是对Java异常处理模型的最佳补充。finally结构使代码总会执行,而不管无异常发生。使用finally可以维护对象的内部状态,并可以清理非内存资源。特别是在关闭数据库连接这方面,如果程序员把数据库连接的close()方法放到finally中,就会大大降低程序出错的几率。
异常处理:
try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
catch 块:用于处理try捕获到的异常。
finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。
在以下4种特殊情况下,finally块不会被执行:
在finally语句块第一行发生了异常。因为在其他行,finally块还是会得到执行
在前面的代码中用了System.exit(int)已退出程序。exit是带参函数 ;若该语句在异常语句之后,finally会执行
程序所在的线程死亡。
关闭CPU。
(3)finalize():是一个方法,它是在对象被垃圾回收之前由Java虚拟机来调用的。
finalize()方法是GC运行机制的一部分,finalize()方法是在GC清理它所从属的对象时被调用的,如果执行它的过程中抛出了无法捕获的异常,GC将终止对改对象的清理,并且该异常会被忽略;直到下一次GC开始清理这个对象时,它的finalize()会被再次调用。
(1)this:代表对象本身,可以理解为:指向对象本身的一个指针。
this的用法在java中大体可以分为3种:
1)普通的直接引用
这种就不用讲了,this相当于是指向当前对象本身:
2)形参与成员名字重名,用this来区分:
3)引用构造函数
this(参数):调用本类中另一种形式的构造方法(应该为构造方法中的第一条语句)
(2)super:代指父类,可以用于调用父类的普通方法和构造方法。
调用父类构造方法:super()(无参构造方法)或 super(参数)(有参构造方法)
调用父类普通方法:super.方法名(参数)
对于不想进行序列化的变量,使用transient关键字修饰。
transient关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被transient修饰的变量值不会被持久化和恢复。transient只能修饰变量,不能修饰类和方法。
方法1:通过 Scanner
- Scanner sc = new Scanner(System.in);
- String s \= sc.nextLine();
- input.close();
方法2:通过 BufferedReader
BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); String s \= input.readLine();
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。