Java 基础
1. equals与==的区别
==
如果是基本类型,==表示判断它们值是否相等;
如果是引用对象,==表示判断两个对象指向的内存地址是否相同。
equals
如果是字符串,表示判断字符串内容是否相同;
如果是object对象的方法,比较的也是引用的内存地址值;
如果自己的类重写equals方法,可以自定义两个对象是否相等。
2. final, finally, finalize 的区别
final 用于修饰属性,方法和类, 分别表示属性不能被重新赋值, 方法不可被覆盖, 类不可被继承.
finally 是异常处理语句结构的一部分,一般以ty-catch-finally出现,finally代码块表示总是被执行.
finalize 是Object类的一个方法,该方法一般由垃圾回收器来调用,当我们调用System.gc() 方法的时候,由垃圾回收器调用finalize()方法,回收垃圾,JVM并不保证此方法总被调用.
3. 重载和重写的区别
重写必须继承,重载不用。
重载表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同)
重写表示子类中的方法与父类中的某个方法的名称和参数完全相同啦,通过子类实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全相同的方法给覆盖了,这是面向对象编程的多态性的一种表现。
重写的方法修饰符大于等于父类的方法,即访问权限只能比父类的更大,不能更小,而重载和修饰符无关。
重写覆盖的方法中,只能比父类抛出更少的异常,或者是抛出父类抛出的异常的子异常,因为不能坑爹啊哈哈~
4. 两个对象的hashCode()相同,则 equals()是否也一定为 true?
两个对象equals相等,则它们的hashcode必须相等,如果两个对象的hashCode()相同,则equals()不一定为true。
hashCode 的常规协定:
在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
两个对象的equals()相等,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
两个对象的equals()不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不要求一定生成不同的整数结果。但是,为不相等的对象生成不同整数结果可以提高哈希表的性能。
5. 抽象类和接口有什么区别
抽象类要被子类继承,接口要被子类实现。
抽象类可以有构造方法,接口中不能有构造方法。
抽象类中可以有普通成员变量,接口中没有普通成员变量,它的变量只能是公共的静态的常量
一个类可以实现多个接口,但是只能继承一个父类,这个父类可以是抽象类。
接口只能做方法声明,抽象类中可以作方法声明,也可以做方法实现。
抽象级别(从高到低):接口>抽象类>实现类。
抽象类主要是用来抽象类别,接口主要是用来抽象方法功能。
抽象类的关键字是abstract,接口的关键字是interface
7. String,Stringbuffer,StringBuilder的区别
String:
String类是一个不可变的类,一旦创建就不可以修改。
String是final类,不能被继承
String实现了equals()方法和hashCode()方法
StringBuffer:
继承自AbstractStringBuilder,是可变类。
StringBuffer是线程安全的
可以通过append方法动态构造数据。
StringBuilder:
继承自AbstractStringBuilder,是可变类。
StringBuilder是非线性安全的。
执行效率比StringBuffer高。
8. JAVA中的几种基本数据类型是什么,各自占用多少字节呢
基本类型位数字节
int324
short162
long648
byte81
char162
float324
double648
boolean??
对于boolean,官方文档未明确定义,它依赖于 JVM 厂商的具体实现。逻辑上理解是占用 1位,但是实际中会考虑计算机高效存储因素
感兴趣的小伙伴,可以去看官网
10. String类能被继承吗,为什么。
首先,String是一个final修饰的类,final修饰的类不可以被继承。
need-to-insert-img
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
String类为什么不能被继承呢?
有两个原因:
效率性,String 类作为最常用的类之一,禁止被继承和重写,可以提高效率。
安全性,String 类中有很多调用底层的本地方法,调用了操作系统的 API,如果方法可以重写,可能被植入恶意代码,破坏程序。
11. 说说Java中多态的实现原理
多态机制包括静态多态(编译时多态)和动态多态(运行时多态)
静态多态比如说重载,动态多态一般指在运行时才能确定调用哪个方法。
我们通常所说的多态一般指运行时多态,也就是编译时不确定究竟调用哪个具体方法,一直等到运行时才能确定。
多态实现方式:子类继承父类(extends)和类实现接口(implements)
多态核心之处就在于对父类方法的改写或对接口方法的实现,以取得在运行时不同的执行效果。
Java 里对象方法的调用是依靠类信息里的方法表实现的,对象方法引用调用和接口方法引用调用的大致思想是一样的。当调用对象的某个方法时,JVM查找该对象类的方法表以确定该方法的直接引用地址,有了地址后才真正调用该方法。
举个例子吧,假设有个Fruit父类,一个taste味道方法,两个子类Apple和Pear,如下:
need-to-insert-img
abstract class Fruit { abstract String taste() ;}class Apple extends Fruit { @Override String taste() { return "酸酸的"; }}class Pear extends Fruit { @Override String taste() { return "甜甜的"; }}public class Test { public static void main(String[] args) { Fruit f = new Apple(); System.out.println(f.taste()); }}
程序运行,当调用对象Fruit f的方法taste时,JVM查找Fruit对象类的方法表以确定taste方法的直接引用地址,到底来自Apple还是Pear,确定后才真正调用对应子类的taste方法,
12. Java泛型和类型擦除
这个面试题,可以看我这篇文章哈~
13. int和Integer 有什么区别,还有Integer缓存的实现
这里考察3个知识点吧:
int 是基本数据类型,interger 是 int 的封装类
int 默认值为 0 ,而interger 默认值为 null, Interger使用需要判空处理
Integer的缓存机制:为了节省内存和提高性能,Integer类在内部通过使用相同的对象引用实现缓存和重用,Integer类默认在-128 ~ 127 之间,可以通过 -XX:AutoBoxCacheMax进行修改,且这种机制仅在自动装箱的时候有用,在使用构造器创建Integer对象时无用。
看个Integer的缓存的例子,加深一下印象哈:
need-to-insert-img
Integer a = 10;Integer b = 10;Integer c = 129;Integer d = 129;System.out.println(a == b);System.out.println(c == d);输出结果:truefalse
14. 说说反射的用途及实现原理,Java获取反射的三种方法
这道面试题,看我这篇文章哈:谈谈Java反射:从入门到实践,再到原理
Java获取反射的三种方法:
第一种,使用 Class.forName 静态方法。
第二种,使用类的.class 方法
第三种,使用实例对象的 getClass() 方法。
15. 面向对象的特征
面向对象的三大特征:
封装
继承
多态
16. &和&&的区别
按位与, a&b 表示把a和b都转换成二进制数,再进行与的运算;
&和&&都是逻辑运算符号,&&又叫短路运算符
逻辑与,a&& b ,a&b 都表示当且仅当两个操作数均为 true时,其结果才为 true,否则为false。
逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true,整个表达式的值才是true。但是,&&之所以称为短路运算,是因为如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。
17. Java中IO流分为几种?
Java中的流分为两种:一种是字节流,另一种是字符流。
IO流分别由四个抽象类来表示(两输入两输出):InputStream,OutputStream,Reader,Writer。
18. 讲讲类的实例化顺序,比如父类静态数据,构造函数,子类静态数据,构造函数。
直接看个例子吧:
need-to-insert-img
public class Parent { { System.out.println("父类非静态代码块"); } static { System.out.println("父类静态块"); } public Parent() { System.out.println("父类构造器"); }}public class Son extends Parent { public Son() { System.out.println("子类构造器"); } static { System.out.println("子类静态代码块"); } { System.out.println("子类非静态代码块"); }}public class Test { public static void main(String[] args) { Son son = new Son(); }}
运行结果:
need-to-insert-img
父类静态块子类静态代码块父类非静态代码块父类构造器子类非静态代码块子类构造器
所以,类实例化顺序为: 父类静态代码块/静态域->子类静态代码块/静态域 -> 父类非静态代码块 -> 父类构造器 -> 子类非静态代码块 -> 子类构造器
19. Java创建对象有几种方式
Java创建对象有5种方式
用new语句创建对象。
使用反射,使用Class.newInstance()创建对象/调用类对象的构造方法——Constructor
调用对象的clone()方法。
运用反序列化手段,调用java.io.ObjectInputStream对象的readObject()方法.
使用Unsafe
22. notify()和 notifyAll()有什么区别?
notify是唤醒一个处于该对象wait的线程,而notifyAll是唤醒所有处于该对象wait的线程。
但是唤醒不等于就能执行了,需要得到锁对象才能有权利继续执行,而锁只有一把,所以多个线程被唤醒时需要争取该锁。
24. 谈谈Java的异常层次结构
从前从前,有位老人,他的名字叫Throwable,他生了两个儿子,大儿子叫Error,二儿子叫Exception。
Error
表示编译时或者系统错误,如虚拟机相关的错误,OutOfMemoryError等,error是无法处理的。
Exception
代码异常,Java程序员关心的基类型通常是Exception。它能被程序本身可以处理,这也是它跟Error的区别。
它可以分为RuntimeException(运行时异常)和CheckedException(可检查的异常)。
常见的RuntimeException异常:
need-to-insert-img
- NullPointerException 空指针异常- ArithmeticException 出现异常的运算条件时,抛出此异常- IndexOutOfBoundsException 数组索引越界异常- ClassNotFoundException 找不到类异常- IllegalArgumentException(非法参数异常)
常见的 Checked Exception 异常:
need-to-insert-img
- IOException (操作输入流和输出流时可能出现的异常)- ClassCastException(类型转换异常类)
Checked Exception就是编译器要求你必须处置的异常。
与之相反的是,Unchecked Exceptions,它指编译器不要求强制处置的异常,它包括Error和RuntimeException 以及他们的子类。
28. JDK动态代理与cglib实现的区别
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
JDK动态代理只能对实现了接口的类生成代理,而不能针对类
cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。因为是继承,所以该类或方法最好不要声明成final
网上这篇文章写得不错,描述Java动态代理的几种实现方式,分别说出相应的优缺点
30. 深拷贝和浅拷贝区别
浅拷贝
复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化。
深拷贝
将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变
31. JDK 和 JRE 有什么区别?
JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java 的开发环境和运行环境。
JRE:Java Runtime Environment 的简称,Java 运行环境,为 Java 的运行提供了所需环境。
32. String 类的常用方法都有那些呢?
indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
split():分割字符串,返回一个分割后的字符串数组。
getBytes():返回字符串的 byte 类型数组。
length():返回字符串长度。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
substring():截取字符串。
equals():字符串比较。
33. 谈谈自定义注解的场景及实现
之前我这边有这么一个业务场景,用Redis控制接口调用频率,有使用过自定义注解。
通过 AOP(动态代理机制)给方法添加切面,通过反射来获取方法包含的注解,如果包含自定义关键字注解,就通过Redis进行校验拦截请求。
有关于注解,建议大家看一下java编程思想的注解篇章哈~
36. 什么是值传递和引用传递?
值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量.
引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。 所以对引用对象进行操作会同时改变原对象.
37. 可以在static环境中访问非static变量吗?
static变量在Java中是属于类的,它在所有的实例中的值是一样的。当类被Java虚拟机载入的时候,会对static变量进行初始化。因为静态的成员属于类,随着类的加载而加载到静态方法区内存,当类加载时,此时不一定有实例创建,没有实例,就不可以访问非静态的成员。类的加载先于实例的创建,因此静态环境中,不可以访问非静态!
38. Java支持多继承么,为什么?
不支持多继承,原因:
安全性的考虑,如果子类继承的多个父类里面有相同的方法或者属性,子类将不知道具体要继承哪个。
Java提供了接口和内部类以达到实现多继承功能,弥补单继承的缺陷。
40. 构造器是否可被重写?
构造器是不能被继承的,因为每个类的类名都不相同,而构造器名称与类名相同,所以谈不上继承。 又由于构造器不能被继承,所以相应的就不能被重写了。
41. char型变量中能不能存贮一个中文汉字,为什么?
在Java中,char类型占2个字节,而且Java默认采用Unicode编码,一个Unicode码是16位,所以一个Unicode码占两个字节,Java中无论汉子还是英文字母都是用Unicode编码来表示的。所以,在Java中,char类型变量可以存储一个中文汉字。
need-to-insert-img
char ch = '啦';System.out.println("char:" + ch);
43. object中定义了哪些方法?
getClass(); 获取类结构信息
hashCode() 获取哈希码
equals(Object) 默认比较对象的地址值是否相等,子类可以重写比较规则
clone() 用于对象克隆
toString() 把对象转变成字符串
notify() 多线程中唤醒功能
notifyAll() 多线程中唤醒所有等待线程的功能
wait() 让持有对象锁的线程进入等待
wait(long timeout) 让持有对象锁的线程进入等待,设置超时毫秒数时间
wait(long timeout, int nanos) 让持有对象锁的线程进入等待,设置超时纳秒数时间
finalize() 垃圾回收前执行的方法
44. hashCode的作用是什么?
hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;
如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;
如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点;
两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中.
这篇文章讲得挺详细的:Java中hashCode的作用
50. 谈谈final在java中的作用?
final 修饰的类叫最终类,该类不能被继承。
final 修饰的方法不能被重写。
final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。
52. String属于基础的数据类型吗?
String 不属于基础类型,基础类型有 8 种:byte、boolean、char、short、int、float、long、double,而 String 属于对象。
54. 描述动态代理的几种实现方式,它们分别有什么优缺点
JDK动态代理
CGLIB动态代理
JDK原声动态代理时java原声支持的、不需要任何外部依赖、但是它只能基于接口进行代理
CGLIB通过继承的方式进行代理、无论目标对象没有没实现接口都可以代理,但是无法处理final的情况
55. 在自己的代码中,如果创建一个java.lang.String类,这个类是否可以被类加载器加载?为什么。
不可以。因为JDK处于安全性的考虑,基于双亲委派模型,优先加载JDK的String类,如果java.lang.String已经加载,便不会再次被加载。
56. 谈谈你对java.lang.Object对象中hashCode和equals方法的理解。在什么场景下需要重新实现这两个方法。
在我们的业务系统中判断对象时有时候需要的不是一种严格意义上的相等,而是一种业务上的对象相等。在这种情况下,原生的equals方法就不能满足我们的需求了,所以这个时候我们需要重写equals方法,来满足我们的业务系统上的需求。
那么为什么在重写equals方法的时候需要重写hashCode方法呢? 如果只重写了equals方法而没有重写hashCode方法的话,则会违反约定的第二条:相等的对象必须具有相等的散列码.所以hashCode和equals方法都需要重写
可以看网上这篇文章哈:java为什么要重写hashCode和equals方法
59. java8的新特性。
Lambda 表达式:Lambda允许把函数作为一个方法的参数
Stream API :新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中
方法引用:方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。
默认方法:默认方法就是一个在接口里面有了一个实现的方法。
Optional 类 :Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
Date Time API :加强对日期与时间的处理。
61. break和continue有什么区别?
break可以使流程跳出switch语句体,也可以在循环结构终止本层循环体,从而提前结束本层循环。
continue的作用是跳过本次循环体中余下尚未执行的语句,立即进行下一次的循环条件判定,可以理解为仅结束本次循环
70. String s = new String("jay");创建了几个字符串对象?
一个或两个
第一次调用 new String("jay"); 时,会在堆内存中创建一个字符串对象,同时在字符串常量池中创建一个对象 "jay"
第二次调用 new String("jay"); 时,只会在堆内存中创建一个字符串对象,指向之前在字符串常量池中创建的 "jay"
可以看老王这篇文章,很清晰~ 别再问我 new 字符串创建了几个对象了!我来证明给你看!
74. 接口可否继承接口?抽象类是否可实现接口?抽象类是否可继承实体类?
都可以的
78. 同步和异步有什么区别?
同步,可以理解为在执行完一个函数或方法之后,一直等待系统返回值或消息,这时程序是出于阻塞的,只有接收到返回的值或消息后才往下执行其他的命令。
异步,执行完函数或方法后,不必阻塞性地等待返回值或消息,只需要向系统委托一个异步过程,那么当系统接收到返回值或消息时,系统会自动触发委托的异步过程,从而完成一个完整的流程。
同步,就是实时处理(如打电话)
异步,就是分时处理(如收发短信)
参考这篇文章~ 同步和异步的区别
79. 实际开发中,Java一般使用什么数据类型来代表价格?
java中使用BigDecimal来表示价格是比较好的。
可以看这篇文章,写得非常好 老大说:谁要再用double定义商品金额,就自己收拾东西走
80. 64 位 JVM 中,int 的长度是多数?
int数据类型占4个字节 32位,跟JVM位数没关系的
81 hashCode()和equals()方法的重要性体现在什么地方?
Java中的HashMap使用hashCode()和equals()方法来确定键值对的索引,当根据键获取值的时候也会用到这两个方法。如果没有正确的实现这两个方法,两个不同的键可能会有相同的hash值,因此,可能会被集合认为是相等的。而且,这两个方法也用来发现重复元素。所以这两个方法的实现对HashMap的精确性和正确性是至关重要的。
123. 创建线程有几种不同的方式
继承Thread类
实现Runnable接口
使用Executor框架来创建线程池