switch 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 String 上
在jdk1.5之前只支持byte,short,char,int。jdk1.5引入枚举,switch支持枚举。jdk1.7开始支持String。long在所有版本中都不支持。
用最有效的方式计算2*8
位运算。2 << 3(左移3位相当于乘以2的3次方,右移3位相当于除以2的三次方)
Math.round(11.5) 等于多少?Math.round(-11.5)等于多少
Math.round是将值 + 0.5,向下取值。所以Math.round(11.5) = 12,Math.round(-11.5) = -11。
float f = 3.4 是否正确
&emsp 不正确,3.4是双精度数,将双精度数(double)赋值给浮点型(fload)属于向下转型,会造成精度损失,因此需要强制类型转换fload f = 3.4f或者 fload f = (fload)3.4;
short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗
对于 short s1 = 1; s1 = s1 + 1;由于 1 是 int 类型,因此 s1+1 运算结果也是 int型,需要强制转换类型才能赋值给 short 型。
而 short s1 = 1; s1 += 1;可以正确编译,因为 s1+= 1;相当于 s1 = (short(s1 + 1);其中有隐含的强制类型转换
。
访问修饰符 public,private,protected,以及不写(默认)时的区别
访问修饰符是java对于封装特性提供的语法支持。
private:在同一个类中可见。
default:在同一个包中可见。
protected :在同一个包内的类可见。不同包下的子类可见。
public:对所有类和所有子类可见。
final 有什么用?
被final修饰的类不可以被继承
被final修饰的方法不可以被重写
被final修饰的变量不可以被改变,被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的
final finally finalize区别
final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值。
finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾回收器来调用
,当我们调用System.gc() 方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的最后判断。
break ,continue ,return 的区别及作用
break 跳出总上一层循环,不再执行循环(结束当前的循环体)
continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件)
return 程序返回,不再执行下面的代码(结束当前的方法 直接返回)
在 Java 中,如何跳出当前的多重嵌套循环
可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break 语句,即可跳出外层循环。例如:
public static void main(String[] args) {
ok:
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
System.out.println("i=" + i + ",j=" + j);
if (j == 5) {
break ok;
}
}
}
}
抽象类能使用 final 修饰吗?
不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类
成员变量与局部变量的区别有哪些
作用域:
成员变量针对整个类有效
局部变量只针对某个范围有效(一般指方法内)
存储位置:
成员变量:随着对象的创建存在,随着对象的消失消失,存储在堆内存中。
局部变量:随着方法的调用,存储在栈内存中。当方法调用结束,就自动释放。
初始值:
成员变量:有默认初始值。
局部变量:没有默认初始值,必须赋值。
声明周期:
成员变量:随着对象的创建而存在,随着对象的消失而消失。
局部变量:当方法调用完,就自动释放。
静态变量和实例变量区别
static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
还有一点就是static成员变量的初始化顺序按照定义的顺序进行初始化。
在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?
帮助子类做初始化工作。
什么是内部类?
在Java中,可以将一个类的定义放在另外一个类的定义内部,这就是内部类。内部类本身就是类的一个属性,与其他属性定义方式一致。
静态内部类:
被static修饰的内部类
成员内部内:
&mesp; 没有被static修饰的内部类
局部内部类:
&mesp; 定义在方法内的内部类
匿名内部类:
&mesp; 没有名字的内部类
== 和 equals 的区别是什么
== : 它的作用是判断两个对象的地址是不是相等
。即,判断两个对象是不是同一个对象。(基本数据类型 == 比较的是值,引用数据类型 == 比较的是内存地址)
equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
(1)情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。
(2)情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)
String中的equals方法是被重写过的,因为object的equals方法是比较的对象的内存地址,而String的equals方法比较的是对象的值。
当创建String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个String对象。
hashCode 与 equals
子问题:
- HashSet如何检查重复?
- 两个对象hashcode相同,那么equals就一定为true?
- hashcode和equals方法的关系
- 你重写过hashcode和equals方法吗?为什么重写equals时必须重写hashcode方法?
hashcode:
hashcode()的作用是获取哈希值,也称为散列码。它实际返回一个int整数。 这个哈希码的作用是确定改对象在哈希表中的索引位置
。hashCode()定义在jdk的Object.java中。这就意味着java中的任何类都包含有hashCode()函数。
散列表存储的键值对,它的特点是:根据键快速的检索出对应的值。这其中就利用到了散列码。
对于问题1:
当我们把对象加入HashSet时,HashSet会先计算对象的hashcode值判断对象加入的位置,同时也会和其他对象的hashcode值做比较,如果没有相符的hashcode,hashset会假设对象没有重复出现。如果有相同的hashcode值的对象,这时会调用equals方法来检查hashcode相等的对象是否相同。如果相同,hashset不会让加入操作成功。如果不同,就会重新散列到其他位置。
对于问题2:
如果两个对象相等,那么他们的hashcode一定是相同的。
两个对象相等,对两个对象分别调用equals方法返回都是true。
两个对象有相同的hashcode,它们不一定是相等的。hashcode会存在hash冲突。对于hash相同的两个对象不一定相等。
对于问题4:
hashcode()默认行为是堆上的对象产生独特值
。如果没有重写hashcode(),则改class的两个对象无论如何都不会相等(即使这两个对象执行相同的数据)。
也就是说如果不重写hashcode,就会出现equals相同的两个对象,但是hashcode不相等的情况(hashcode会默认走的Object中的hashcode方法)。
为什么 Java 中只有值传递?
java语言中只有值传递。方法得到的是所有参数值的一个拷贝
,也就是说,方法不能修改传递给它的任何参数变量的内容
。
值传递:
指的是在方法调用时,传递的参数是按值的拷贝传递,传递的是值的拷贝,也就是说传递后就互不相关了。
引用传递:
指的是在方法调用时,传递的参数是按引用进行传递,其实传递的引用的地址,也就是变量所对应的内存空间的地址。传递的是值的引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间)。
JDK 中常用的包有哪些
java.lang:这个是系统的基础类;
java.io:这里面是所有输入输出有关的类,比如文件操作等;
java.nio:为了完善 io 包中的功能,提高 io 包中性能而写的一个新包;
java.net:这里面是与网络有关的类;
java.util:这个是系统辅助类,特别是集合类;
java.sql:这个是数据库操作的类。
java 中 IO 流分为几种?
按照流的流向分,可以分为输入流和输出流;
按照操作单元划分,可以划分为字节流和字符流;
按照流的角色划分为节点流和处理流。
InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流
BIO,NIO,AIO 有什么区别?
BIO同步阻塞IO。数据读取写入必须阻塞在一个线程内等待其完成。
NIO同步非阻塞IO。java.nio包,提供了channel,bytebuffer,selector等抽象。数据读取写入线程不需要一直阻塞。
AIO异步非阻塞IO。异步IO是基于事件和回调机制实现的。等IO操作结束会通知调用线程。
什么是反射机制?
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法
的功能称为java语言的反射机制。
字符型常量和字符串常量的区别
- 形式上: 字符常量是单引号引起的一个字符。字符串常量是双引号引起的若干个字符
- 含义上: 字符常量相当于一个整形值(ASCII值),可以参加表达式运算 字符串常量代表一个地址值(该字符串在内存中存放位置)
- 占内存大小 字符常量只占一个字节 字符串常量占若干个字节(至少一个字符结束标志)
什么是字符串常量池?
字符串常量池位于堆内存
中,专门用来存储字符串常量
,可以提高内存的使用率,避免开辟多块空间存储相同的字符串。在创建字符串的时候,首先会检查字符串常量池中是否存在相同的字符串,有则返回它的引用,不过不存在,则实例化一个字符串放到池中,并返回其引用。
String有哪些特性
不变量。String是只读字符串,对它进行的任何操作实际上都是创建一个新的对象,再把指针指向改独享。
常量池优化。String对象创建后,会再字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用。
final。使用final来定义String类,表示String类不能被集成,提高了安全性。
String str="i"与 String str=new String(“i”)一样吗?
不一样,因为内存的分配方式不一样
。String str="i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。
String s = new String(“xyz”);创建了几个字符串对象
两个对象,一个是静态区的"xyz"
,一个是用new创建在堆上的对象
。
String str1 = “hello”; //str1指向静态区
String str2 = new String(“hello”); //str2指向堆上的对象
如何将字符串进行反转
使用 StringBuilder
或者 stringBuffer
的 reverse()
方法。
String 类的常用方法都有那些?
indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
split():分割字符串,返回一个分割后的字符串数组。
getBytes():返回字符串的 byte 类型数组。
length():返回字符串长度。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
substring():截取字符串。
equals():字符串比较。
在使用 HashMap 的时候,用 String 做 key 有什么好处?
HashMap 内部实现是通过 key 的 hashcode 来确定 value 的存储位置,因为字符串是不可变的,所以当创建字符串时,它的 hashcode 被缓存下来,不需要再次计算,所以相比于其他对象更快。
String和StringBuffer、StringBuilder的区别是什么?String为什么是不可变的
可变性:
String是不可变的。StringBuffer和StringBuilder是可变的。
线程安全性:
String是不可变得,线程安全。StringBuffer对方法加了同步锁,线程安全。StringBuilder是非线程安全的。
性能:
在不存在线程安全问题情况下,StringBuilder性能比StringBuffer搞10%-50%。
自动装箱与拆箱
装箱:将基本类型用它们对应的引用类型包装起来;
拆箱:将包装类型转换为基本数据类型
int 和 Integer 有什么区别
int是基本类型,Integer是int的包装类型。
int的默认值是0,Integer的默认值是null。
int无法进行序列化,类型转换。Integer可以。
int存储在栈上,Integer存储在堆上。
Integer a= 127 与 Integer b = 127相等吗?
相同,true。
Integer中会缓存-128-127之间的值。自动装箱的时候,值在这个范围之间,就不会new新的Integer对象,而是直接引用常量池中的Integer对象。
Java中引用
-
强引用(StrongReference)
比如Object strongReference = new Object();
强引用有引用变量指向时永远不会被垃圾回收
,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。
如果强引用对象不使用时,需要弱化从而使GC
能够回收。strongReference = null
; -
软引用(SoftReference)
如果一个对象具有软引用
,内存空间足够,垃圾回收器就不会回收它
;
如果内存空间不足了
,就会回收这些对象的内存
。只要垃圾回收器没有回收它,该对象就可以被程序使用。
软引用可以和引用队列
(ReferenceQueue
)联合使用。如果软引用对象
被垃圾回收
,Java虚拟机就把这个软引用
加入到与之关联的引用队列
中。 -
弱引用(WeakReference)
弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。下面是使用示例:
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。 -
虚引用
虚引用,就是形同虚设。与其他几种引用都不同,虚引用并不会决定对象的声明周期
。如果一个对象仅持有虚引用,那么它就和没有持有任何引用
一样。在任何时候都可能被垃圾回收器回收。
虚引用必须和引用队列联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。
ReferenceQueue
引用队列。当GC准备回收一个对象的时候,如果发现它还仅有软引用(弱引用,或者虚引用)指向它。就会在回收该对象之前
,把这个引用加入到与之关联的引用队列中
。
为什么要使用内部类?
一般来说,内部类继承自某个类或实现某个接口,内部类的代码操作创建其的外围类的对象。可以认为内部类提供了某种进入其外围类的窗口。
在Java中,有以下几种内部类。
- 普通内部类
public class Demo {
// 普通内部类
public class DemoRunnable implements Runnable {
@Override
public void run() {
}
}
}
- 匿名内部类
public class Demo {
// 匿名内部类
private Runnable runnable = new Runnable() {
@Override
public void run() {
}
};
}
- 局部内部类
public class Demo {
// 局部内部类
public void work() {
class InnerRunnable implements Runnable {
@Override
public void run() {
}
}
InnerRunnable runnable = new InnerRunnable();
}
}
一个设计的出现,肯定是为了解决开发中的某个痛点问题。内部类有以下优点:
- 内部类能够更好的封装,屏蔽实现细节
- 内部类可以天然的访问外部类成员变量
内部类编译后,构造函数默认传入外部类。
原因
:
- 内部类使得多重继承的解决方案变得完整。内部类允许继承多个非接口类型。也就是说
内部类可以解决 java 单继承的缺陷
。使用内部类可以实现多重继承
。
场景:
比如说遗传。既要继承父类的特征,也要继承母亲的行为特称。java提供了两种方式解决。接口
或者内部类
。
可以在son类中定义两个内部类,分别继承Father,Mother。
class innerOne extends Father{
public void Strong() {
super.getStrong();
}
}
class innerTwo extends Mother{
public void kind() {
super.kind();
}
}
public void getStrion() {
new innerOne().Strong();
}
public void getKin() {
new innerTwo().kind();
}
public static void main(String[] args) {
Son son = new Son();
son.getStrion();
son.getKin();
}
- 当我们需要定义一个回调函数却不想写大量代码的时候我们可以选择使用匿名内部类来实现。使用
匿名内部类,可以优雅的实现回调
。如果使用直接方法的回调,最大的问题是暴漏了回调接口,也就是说回调接口可以用于回调,也可能用于其他的业务访问。这个在设计层面是不允许的。