Java 面试中最容易忽略的细节,你中了几个?避坑指南送你
今日分享开始啦,请大家多多指教~
学习 Java 要明确自己的需求,知道自己要做什么;分析思路,知道自己要怎么做。确定步骤,每一个思路部分用到哪些方法和对象。最后用代码实现,用具体的 java 语言把思路体现出来。
1.Java 的 8 种基本数据类型 及其大小?
Java 要确定每种基本类型所占存储空间的大小,它们的大小并不像其他大多数语言那样随机器硬件架构的变化而变化。这种所占存储空间大小的不变性是 Java 程序比用其他大多数语言编写的程序更具可移植性的原因之一。
2.Java 基本类型与引用类型的区别?
基本类型保存原始值,引用类型保存的是引用值(引用值就是指对象在堆中所处的位置/地址)
3.自动装箱和拆箱是什么?
自动装箱是 Java 编译器在基本数据类型和对应的对象包装类型之间做的一个转化。
比如:把 int 转化成 Integer,double 转化成 Double,等等。反之就是自动拆箱。
原始类型: boolean,char,byte,short,int,long,float,double
封装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
装箱:将基本类型用它们对应的引用类型包装起来;
拆箱:将包装类型转换为基本数据类型;
4.字节与字符的区别?
字节是存储容量的基本单位。
字符是数字,字母,汉字以及其他语言的各种符号。
1 字节=8 个二进制单位;字符由一个字节或多个字节的二进制单位组成。
5.面向对象和面向过程的区别
简便回答:
1.面向对象因为有封装、继承和多态等特性,所以是易维护、易复用和易扩展的。
2.面向过程性能比面向对象高。因为类调用时实例化开销大,消耗资源。常用在单片机、嵌入式等。
3.最典型的例子就是。雕版印刷是面向过程,活字印刷是面向对象。
详细介绍
面向过程:面向过程性能比面向对象高。 因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix 等一般采用面向过程开发。但是,面向过程没有面向对象易维护、易复用、易扩展。
面向对象:面向对象易维护、易复用、易扩展。 因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,面向对象性能比面向过程低。
实例:
面向对象如活字印刷,面向过程如雕版印刷,面向过程的时候你需要从头到尾考虑每一个细节,比如你要刻下学而时习之,不亦说乎这几个字,如果是雕版印刷,你肯定要一环扣一环,“学”后面要刻“而”,“而”后面要刻好“时”,一旦你想改成学而时习之,我不亦乐乎。则原来那一块雕版就得作废,重头改。
而面向对象则把每一个字看作一个对象,类似于活字印刷,你如果想加字,你只要再多刻一个“我”就可以了,其它写好的就不用改了。并且在这里我引申出以下几个概念
一、要改,只需更改要改之字,此为可维护;
二、这些字并非用完这次就无用,完全可以在后来的印刷中重复使用,此乃可复用;
三、此诗若要加字,只需另刻字加入即可,这是可扩展;
6. JDK 和 JRE 的区别?
JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java 的开发环境和运行环境。
JRE:Java Runtime Environment 的简称,Java 运行环境,为 Java 的运行提供了所需环境。 具体来说 JDK 其实包含了 JRE,同时还包含了编译 Java 源码的编译器 Javac,还包含了很多 Java 程序调试和分析的工具。
简单来说:如果你需要运行 Java 程序,只需安装 JRE 就可以了,如果你需要编写 Java 程序,需要安装 JDK。
JDK(Java Development Kit),Java 语言的软件开发工具包。
两个主要组件:
javac-编译器,将源程序转成字节码
java-运行编译后的 java 程序(.class 后缀的)
JRE(Java Runtime Environment)
包括 Java 虚拟机(JVM)、Java 核心类库和支持文件
如果只需要运行 Java 程序,下载并安装 JRE 即可
如果要开发 Java 软件,需要下载 JDK
在 JDK 中附带有 JRE
7.重载和重写的区别?
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。
重载发生在一个类中,同名的方法如果有不同的参数列表(类型不同、个数不同、顺序不同)则视为重载。重载对返回类型没有特殊的要求。
重写发生在子类与父类之间,重写要求子类重写之后的方法与父类被重写方法有相同的返回类型,比父类被重写方法更好的访问范围,不能比父类被重写方法声明更多的异常(里氏代换原则)。
方法重载的规则:
方法名一致,参数列表中参数的顺序,类型,个数不同。
重载与方法的返回值无关,存在于父类和子类,同类中。
可以抛出不同的异常,可以有不同修饰符。
方法重写的规则:
参数列表、方法名、返回值类型必须完全一致;
构造方法不能被重写;
声明为 final 的方法不能被重写;
声明为 static 的方法不存在重写(重写和多态联合才有意义);
访问权限不能比父类更低;
重写之后的方法不能抛出更宽泛的异常;
8. String 和 StringBuffer、StringBuilder 的区别是什么?
简便回答
1.String 是不可变的,StringBuffer 和 StringBuilder 都是可变的。
2.String 不可变,可视为常量,所以线程安全;StringBuffer 使用时加了同步锁,所以是线程安全的;StringBuilder 没加锁,所以不是线程安全的。
3.操作少量数据用 String;单线程字符串缓冲区下操作大量数据用 StringBuilder ;多线程字符串缓冲区下操作大量数据用 StringBuffer。
1.可变性
简单地来说:String 类中使用 final 关键字修饰字符数组来保存字符串,private final char value[],所以 String 对象是不可变的。而 StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串 char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。
StringBuilder 与 StringBuffer 的构造方法都是调用父类构造方法也就是 AbstractStringBuilder 实现的,大家可以自行查阅源码。
AbstractStringBuilder.java
2.线程安全性
String 中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
3.性能
每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
对于三者使用的总结:
操作少量的数据: 适用 String
单线程字符串缓冲区下操作大量数据: 适用 StringBuilder
多线程字符串缓冲区下操作大量数据: 适用 StringBuffer
9.反射 Class.forName 和 classLoader 有什么区别
第一:Class.forName(“className”);
其实这种方法调运的是:Class.forName(className,true,ClassLoader.getCallerClassLoader())方法
参数一:className,需要加载的类的名称。
参数二:true,是否对 class 进行初始化(需要 initialize)
参数三:classLoader,对应的类加载器
第二:ClassLoader.laodClass(“className”);
其实这种方法调运的是:ClassLoader.loadClass(name,false)方法
参数一:name,需要加载的类的名称
参数二:false,这个类加载以后是否需要去连接(不需要 linking)
第三:区别
可见 Class.forName 除了将类的.class 文件加载到 jvm 中之外,还会对类进行解释,执行类中的 static 块。
而 classloader 只干一件事情,就是将.class 文件加载到 jvm 中,不会执行 static 中的内容,只有在 newInstance 才会去执行 static 块。
10.反射的使用场景
代理模式,JDBC 链接数据库,Spring
11.反射的缺点是什么?如何优化?
缺点: java 反射是要解析字节码,将内存中的对象进行解析,包括了一些动态类型,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多!
提高反射性能的方式有哪些?
setAccessible(true),可以防止安全性检查(做这个很费时)
做缓存,把要经常访问的元数据信息放入内存中,class.forName 太耗时
getMethods() 等方法尽量少用,尽量调用 getMethod(name)指定方法的名称,减少遍历次数
12.静态代理模式和动态代理模式的区别
静态: 由程序员创建代理类。在程序运行前要代理的对象就已经指定了。
动态: 在程序运行时运用反射机制动态创建而成。(InvocationHandler 的应用)
13.String 为什么是不可变的?
String 类中使用 final 关键字修饰字符数组来保存字符串,private final char value[],所以 String 对象是不可变的。而 StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串 char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。
14.抽象类和接口的区别是什么?
实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。
构造函数:抽象类可以有构造函数;接口不能有。
实现数量:类可以实现很多个接口;但只能继承一个抽象类【java 只支持单继承】。
访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的抽象方法可以使用 Public 和 Protected 修饰,如果抽象方法修饰符为 Private,则报错:The abstract method 方法名 in type Test can only set a visibility modifier, one of public or protected。
接口中除了 static、final 变量,不能有其他变量,而抽象类中则不一定
设计层面:抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
在抽象类中可以为部分方法提供默认的实现,从而避免了重复实现它们,提高了代码的可重用性,这是抽象类的优势。
15.常见的异常类有哪些?
NullPointerException 空指针异常
ClassNotFoundException 指定类不存在
NumberFormatException 字符串转换为数字异常
IndexOutOfBoundsException 数组下标越界异常
ClassCastException 数据类型转换异常
FileNotFoundException 文件未找到异常
NoSuchMethodException 方法不存在异常
IOException IO 异常
SocketException Socket 异常
16.什么是 Java 序列化?什么情况下需要序列化?
Java 序列化是为了保存各种对象在内存中的状态,并且可以把保存的对象状态再读出来。
以下情况需要使用 Java 序列化:
想把的内存中的对象状态保存到一个文件中或者数据库中时候;
想用套接字在网络上传送对象的时候;
想通过 RMI(远程方法调用)传输对象的时候。
17.普通类和抽象类有哪些区别?
普通类不能包含抽象方法,抽象类可以包含抽象方法。
抽象类是不能被实例化的,就是不能用 new 调出构造方法创建对象,普通类可以直接实例化。
如果一个类继承于抽象类,则该子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为 abstract 类。
18.抽象类能使用 final 修饰吗?
不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类
19.String 类的常用方法都有哪些?
indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
split():分割字符串,返回一个分割后的字符串数组。
getBytes():返回字符串的 byte 类型数组。
length():返回字符串长度。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
substring():截取字符串。
equals():字符串比较。
20.抽象类必须要有抽象方法吗?
不需要,抽象类不一定非要有抽象方法;但是包含一个抽象方法的类一定是抽象类。
21.String str="i"与 String str=new String(“i”)一样吗?
不一样,因为内存的分配方式不一样。String str=“i"的方式,Java 虚拟机会将其分配到常量池中,如果常量池中有"i”,就返回"i"的地址,如果没有就创建"i",然后返回"i"的地址;而 String str=new String(“i”) 则会被分到堆内存中新开辟一块空间。
22.String 属于基础的数据类型吗?
String 不属于基础类型,基础类型有 8 种:byte、boolean、char、short、int、float、long、double,而 String 属于对象。
23. final 在 Java 中有什么作用?
final 修饰的类叫最终类,该类不能被继承。
final 修饰的方法不能被重写。
final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。
24.Java 中的 Math. round(-1. 5) 等于多少?
等于 -1。round()是四舍五入,注意负数 5 是舍的,例如:Math.round(1.5)值是 2,Math.round(-1.5)值是-1。
25.== 与 equals 的区别?
== 解读:
对于基本类型和引用类型 == 的作用效果是不同的,如下所示:
基本类型:比较的是值是否相同; 引用类型:比较的是引用是否相同; 代码示例:
代码解读:因为 x 和 y 指向的是同一个引用,所以 == 也是 true,而 new String()方法则重写开辟了内存空间,所以 == 结果为 false,而 equals 比较的一直是值,所以结果都为 true。
equals 解读:
equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。看下面的代码就明白了。
首先来看默认情况下 equals 比较一个有相同值的对象,代码如下:
输出结果出乎我们的意料,竟然是 false?这是怎么回事,看了 equals 源码就知道了,源码如下:
原来 equals 本质上就是 ==。 那问题来了,两个相同值的 String 对象,为什么返回的是 true?代码如下:
同样的,当我们进入 String 的 equals 方法,找到了答案,代码如下:
原来是 String 重写了 Object 的 equals 方法,把引用比较改成了值比较。
总结 :== 对于基本类型来说是值比较,对于引用类型来说是比较的是引用;而 equals 默认情况下是引用比较,只是很多类重新了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。
26.hashCode 与 equals
hashCode()介绍
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在 JDK 的 Object.java 中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。
散列表存储的是键值对(key-value),它的特点是:能根据“键”快速地检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)
为什么要有 hashCode
当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的 Java 启蒙书《Head first java》第二版)。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。
hashCode()与 equals()的相关规定
1.如果两个对象相等,则 hashcode 一定也是相同的
2.两个对象相等,对两个对象分别调用 equals 方法都返回 true
3.两个对象有相同的 hashcode 值,它们也不一定是相等的
4.因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
5.hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
小结:
首先建立好 Java 基础非常重要,工欲善其事,必先利其器。做任何事情,首先就是要把这个基础准备好,之后就可以去做各种尝试,尝试过程中就能逐渐建立信心。
今日份分享已结束,请大家多多包涵和指点!
评论