目录
1、 Java语言有哪些特点
2、面向对象和面向过程的区别
4、 标识符的命名规则。
5、 instanceof关键字的作用
6、Java自动装箱与拆箱
8、 equals与==的区别
9、 Hashcode的作用
10、String、String StringBuffffer 和 StringBuilder 的区别是什
么?
11、ArrayList和linkedList的区别
12、 HashMap和HashTable的区别
13、 Collection包结构,与Collections的区别
14、 Java的四种引用,强弱软虚
15、 泛型常用特点
16、Java创建对象有几种方式?
17、有没有可能两个不相等的对象有相同的hashcode
18、深拷贝和浅拷贝的区别是什么?
19、fifinal有哪些用法?
20、static都有哪些用法?
21、3*0.1 == 0.3返回值是什么
1、 Java语言有哪些特点
1
、简单易学、有丰富的类库
2
、面向对象(
Java
最重要的特性,让程序耦合度更低,内聚性更高)
3
、与平台无关性(
JVM
是
Java
跨平台使用的根本)
4
、可靠安全
5
、支持多线程
2、面向对象和面向过程的区别
面向过程
:是分析解决问题的步骤,然后用函数把这些步骤一步一步地实现,然后在使用的时候一
一调用则可。性能较高,所以单片机、嵌入式开发等一般采用面向过程开发
面向对象
:是把构成问题的事务分解成各个对象,而建立对象的目的也不是为了完成一个个步骤,
而是为了描述某个事物在解决整个问题的过程中所发生的行为。面向对象有
封装、继承、多态
的特
性,所以易维护、易复用、易扩展。可以设计出低耦合的系统。 但是性能上来说,比面向过程要
低。
3
、八种基本数据类型的大小,以及他们的封装类
注:
1.in堤基本数据类型,Integer是int的封装类,是引用类型。int默认值是0 ,而Integer默认值 是null ,所以Integer能区分出0和null的情况。一旦java看到null ,就知道这个弓I用还没有指向某个 对象,再任何弓I用使用前,必须为_旨定一个对象,否则会报错。
2.基本数据类型在声明时系统会自动给它分配空间,而引用类型声明时只是分配了弓I用空间, 必须通过实例化开辟数据空间之后才可以赋值。数组对象也是一个引用对象,将一个数组赋值给另 一个数组时只是复制了一个引用,所以通过某一个数组所做的修改在另一个数组中也看的见。
虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。田ava虚拟机中没有 任何供boolean值专用的字节码指令,」ava语言表达式所操作的boolean值,在编译之后都使用」ava 虚拟机中的in壤踞类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素 boolean元素占8位。这样我们可以得出boolean类型占了单独使用是4个字节,在数组中又是1个字 节。使用int的原因是,对于当下32位的处理器(CPU )来说,一次处理数据是32位(这里不是指的 是32/64位系统,而是指CPU硬件层面),具有高效存取的特点。
4、 标识符的命名规则。
标识符的含义:唐旨在程序中,我们自己定义的内容,譬如,类的名字,方法名称以及变量名称等 等,都是标识符。
命名规则:(硬性要求)标识符可以包含英文字母,0-9的数字,$以及_标识符不能以数字开头标 识符不是关键字
命名规范:(非硬性要求)类名规范:首字符大写,后面每个单词首字母大写(大驼峰式)。变量 名规范:首字母小写,后面每个单词首字母大写(小驼峰式)。方法名规范:同变量名。
5、 instanceof关键字的作用
instanceof严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例,用法 为:
boolean result = obj instanceof Class
其中obj为一个对象,Class表示一个类或者一个接口,当obj为Class的对象,或者是其直接 或间接子类,或者是其接口的实现类,结果result都返回true ,否则返回false。
注意:编译器会检查obj是否能转换成右边的class类型,如果不能转换则直接报错,如果不能 确定类型,则通过编译,具体看运行时定。
int
i
=
0
;
System
.
out
.
println
(
i
instanceof
Integer
);
//
编译不通过
i
必须是引用类型,不能是基本类型
System
.
out
.
println
(
i
instanceof
Object
);
//
编译不通过
Integer
integer
=
new
Integer
(
1
);
System
.
out
.
println
(
integer
instanceof
Integer
);
//true
//false ,
在
JavaSE
规范 中对
instanceof
运算符的规定就是:如果
obj
为
null
,那么将返
回
false
。
System
.
out
.
println
(
null
instanceof
Object
);
6、Java自动装箱与拆箱
装箱就是自动将基本数据类型转换为包装器类型(int->lnteger):调用方法:Integer的 valueOf(int)方法
拆箱就是自动将包装器类型转换为基本数据类型(lnteger->int)o调用方法:Integer的 intValue 方法
在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行:
Integer i = new Integer(10);
而在从Java SE5开始就提供了自动装箱的特性,如果要生成一个数值为10的Integer对象,只需要 这样就可以了:
Integer i = 10;
面试题
1
:
以下代码会输出什么?
public class
Main
{
public static
void
main
(
String
[]
args
) {
Integer
i1
=
100
;
Integer
i2
=
100
;
Integer
i3
=
200
;
Integer
i4
=
200
;
System
.
out
.
println
(
i1
==
i2
);
System
.
out
.
println
(
i3
==
i4
);
}
}
运行结果:
为什么会出现这样的结果?输出结果表明i1和i2指向的是同一个对象,而i3和i4指向的是不同的对 象。此时只需一看源码便知究竟,下面这段代码是Integer的valueOf方法的具体实现:
public static Integer valueOf(int i) {
if(i >= -128 && i <= IntegerCache.high)
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
其中IntegerCache类的实现为:
private static class IntegerCache {
static final int high;
static final Integer cache[];
static {
final int low = -128;
// high value may be configured by property
int h = 127;
if (integerCacheHighPropValue != null) {
// Use Long.decode here to avoid invoking methods that
// require Integer's autoboxing cache to be initialized
int i = Long.decode(integerCacheHighPropValue).intValue();
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - -low);
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}
从这
2
段代码可以看出,在通过
valueOf
方法创建
Integer
对象的时候,如果数值在
[-128,127]
之间,
便返回指向
IntegerCache.cache
中已经存在的对象的引用;否则创建一个新的
Integer
对象。
上面的代码中
i1
和
i2
的数值为
100
,因此会直接从
cache
中取已经存在的对象,所以
i1
和
i2
指向的是
同一个对象,而
i3
和
i4
则是分别指向不同的对象。
面试题
2
:以下代码输出什么
public class Main {
public static void main(String[] args) {
Double i1 = 100.0;
Double i2 = 100.0;
Double i3 = 200.0;
Double i4 = 200.0;
System.out.println(i1==i2);
System.out.println(i3==i4);
}
}
运行结果:
false
false
原因: 在某个范围内的整型数值的个数是有限的,而浮点数却不是
7
、 重载和重写的区别
重写
(Override)
从字面上看,重写就是 重新写一遍的意思。其实就是在子类中把父类本身有的方法重新写一遍。子
类继承了父类原有的方法,但有时子类并不想原封不动的继承父类中的某个方法,所以在方法名,
参数列表,返回类型
(
除过子类中方法的返回值是父类中方法返回值的子类时
)
都相同的情况下,
对
方法体进行修改或重写,这就是重写。但要注意子类函数的访问修饰权限不能少于父类的。
public class Father {
public static void main(String[] args) {
// TODO Auto-generated method stub
Son s = new Son();
s.sayHello();
}
public void sayHello() {
System.out.println("Hello");
}
}
class Son extends Father{
@Override
public void sayHello() {
// TODO Auto-generated method stub
System.out.println("hello by ");
}
}
重写 总结:
1.
发生在父类与子类之间
2.
方法名,参数列表,返回类型(除过子类中方法的返回类型
是父类中返回类型的子类)必须相同
3.
访问修饰符的限制一定要大于被重写方法的访问修饰符
(
public>protected>default>private) 4.
重写方法一定不能抛出新的检查异常或者比被重写方法申
明更加宽泛的检查型异常
重载(
Overload
)
在一个类中,同名的方法如果有不同的参数列表(
参数类型不同、参数个数不同甚至是参数顺序不
同
)则视为重载。同时,重载对返回类型没有要求,可以相同也可以不同,但
不能通过返回类型是
否相同来判断重载
。
public class Father {
public static void main(String[] args) {
// TODO Auto-generated method stub
Father s = new Father();
s.sayHello();
s.sayHello("wintershii");
}
public void sayHello() {
System.out.println("Hello");
}
public void sayHello(String name) {
System.out.println("Hello" + " " + name);
}
}
重载 总结:
1.
重载
Overload
是一个类中多态性的一种表现
2.
重载要求同名方法的参数列表不同
(
参
数类型,参数个数甚至是参数顺序
) 3.
重载的时候,返回值类型可以相同也可以不相同。无法以返回
型别作为重载函数的区分标准
8、 equals与==的区别
==
:
==
比较的是变量
(
栈
)
内存中存放的对象的
(
堆
)
内存地址,用来判断两个对象的地址是否相同,即是
否是指相同一个对象。比较的是真正意义上的指针操作。
1
、比较的是操作符两端的操作数是否是同一个对象。
2
、两边的操作数必须是同一类型的(可以是
父子类之间)才能编译通过。
3
、比较的是地址,如果是具体的阿拉伯数字的比较,值相等则为
true
,如:
int a=10
与
long b=10L
与
double c=10.0
都是相同的(为
true
),因为他们都指向地
址为
10
的堆。
equals
:
equals
用来比较的是两个对象的内容是否相等,由于所有的类都是继承自
java.lang.Object
类的,所
以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是
Object
类中的方法,而
Object
中的
equals
方法返回的却是
==
的判断。
总结:
所有比较是否相等时,都是用
equals
并且在对常量相比较时,把常量写在前面,因为使用
object
的
equals object
可能为
null
则空指针
在阿里的代码规范中只使用
equals
,阿里插件默认会识别,并可以快速修改,推荐安装阿里插件来
排查老代码使用
“==”
,替换成
equals
9、 Hashcode的作用
java
的集合有两类,一类是
List
,还有一类是
Set
。前者有序可重复,后者无序不重复。当我们在
set
中插入的时候怎么判断是否已经存在该元素呢,可以通过
equals
方法。但是如果元素太多,用这样
的方法就会比较满。
于是有人发明了哈希算法来提高集合中查找元素的效率。 这种方式将集合分成若干个存储区域,每
个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的
哈希码就可以确定该对象应该存储的那个区域。
hashCode
方法可以这样理解:它返回的就是根据对象的内存地址换算出的一个值。这样一来,当
集合要添加新的元素时,先调用这个元素的
hashCode
方法,就一下子能定位到它应该放置的物理
位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如
果这个位置上已经有元素了,就调用它的
equals
方法与新元素进行比较,相同的话就不存了,不相
同就散列其它的地址。这样一来实际调用
equals
方法的次数就大大降低了,几乎只需要一两次。
10、String、String StringBuffffer 和 StringBuilder 的区别是什
么?
String
是只读字符串,它并不是基本数据类型,而是一个对象。从底层源码来看是一个
fifinal
类型的
字符数组,所引用的字符串不能被改变,一经定义,无法再增删改。每次对
String
的操作都会生成
新的
String
对象。
private final
char
value
[];
每次
+
操作 :
隐式在堆上
new
了一个跟原字符串相同的
StringBuilder
对象,再调用
append
方法 拼
接
+
后面的字符。
StringBuffffer
和
StringBuilder
他们两都继承了
AbstractStringBuilder
抽象类,从
AbstractStringBuilder
抽象类中我们可以看到
/**
* The value is used for character storage.
*/
char
[]
value
;
他们的底层都是可变的字符数组,所以在进行频繁的字符串操作时,建议使用
StringBuffffer
和
StringBuilder
来进行操作。 另外
StringBuffffer
对方法加了同步锁或者对调用的方法加了同步锁,所
以是线程安全的。
StringBuilder
并没有对方法进行加同步锁,所以是非线程安全的。
11、ArrayList和linkedList的区别
Array
(数组)是基于索引
(index)
的数据结构,它使用索引在数组中搜索和读取数据是很快的。
Array
获取数据的时间复杂度是
O(1),
但是要删除数据却是开销很大,因为这需要重排数组中的所有
数据
, (
因为删除数据以后
,
需要把后面所有的数据前移
)
缺点
:
数组初始化必须指定初始化的长度
,
否则报错
例如
:
int
[]
a
=
new
int
[
4
];
//
推介使用
int[]
这种方式初始化
int
c
[]
=
{
23
,
43
,
56
,
78
};
//
长度:
4
,索引范围:
[0,3]
List—
是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式,它继承
Collection
。
List
有两个重要的实现类:
ArrayList
和
LinkedList
ArrayList:
可以看作是能够自动增长容量的数组
ArrayList
的
toArray
方法返回一个数组
ArrayList
的
asList
方法返回一个列表
ArrayList底层的实现是Array, 数组扩容实现
LinkList
是一个双链表
,
在添加和删除元素时具有比
ArrayList
更好的性能
.
但在
get
与
set
方面弱于
ArrayList.
当然
,
这些对比都是指数据量很大或者操作很频繁。
12、 HashMap和HashTable的区别
1
、两者父类不同
HashMap
是继承自
AbstractMap
类,而
Hashtable
是继承自
Dictionary
类。不过它们都实现了同时
实现了
map
、
Cloneable
(可复制)、
Serializable
(可序列化)这三个接口。
2
、对外提供的接口不同
Hashtable
比
HashMap
多提供了
elments()
和
contains()
两个方法。
elments()
方法继承自
Hashtable
的父类
Dictionnary
。
elements()
方法用于返回此
Hashtable
中的
value
的枚举。
contains()
方法判断该
Hashtable
是否包含传入的
value
。它的作用与
containsValue()
一致。事实
上,
contansValue()
就只是调用了一下
contains()
方法。
3
、对
null
的支持不同
Hashtable
:
key
和
value
都不能为
null
。
HashMap
:
key
可以为
null
,但是这样的
key
只能有一个,因为必须保证
key
的唯一性;可以有多个
key
值对应的
value
为
null
。
4
、安全性不同
HashMap
是线程不安全的,在多线程并发的环境下,可能会产生死锁等问题,因此需要开发人员自
己处理多线程的安全问题。
Hashtable
是线程安全的,它的每个方法上都有
synchronized
关键字,因此可直接用于多线程中。
虽然
HashMap
是线程不安全的,但是它的效率远远高于
Hashtable
,这样设计是合理的,因为大部
分的使用场景都是单线程。当需要多线程操作的时候可以使用线程安全的
ConcurrentHashMap
。
ConcurrentHashMap
虽然也是线程安全的,但是它的效率比
Hashtable
要高好多倍。因为
ConcurrentHashMap
使用了分段锁,并不对整个数据进行锁定。
5
、初始容量大小和每次扩充容量大小不同
6
、计算
hash
值的方法不同
13、 Collection包结构,与Collections的区别
Collection
是集合类的上级接口,子接口有
Set
、
List
、
LinkedList
、
ArrayList
、
Vector
、
Stack
、
Set
;
阿里内部资料
Collections
是集合类的一个帮助类,
它包含有各种有关集合操作的静态多态方法,用于实现对各种
集合的搜索、排序、线程安全化等操作。此类不能实例化,就像一个工具类,服务于
Java
的
Collection
框架
14、 Java的四种引用,强弱软虚
强引用
强引用是平常中使用最多的引用,强引用在程序内存不足(
OOM
)的时候也不会被回收,使用
方式:
String
str
=
new
String
(
"str"
);
System
.
out
.
println
(
str
);
软引用
软引用在程序内存不足时,会被回收,使用方式:
//
注意:
wrf
这个引用也是强引用,它是指向
SoftReference
这个对象的,
//
这里的软引用指的是指向
new String("str")
的引用,也就是
SoftReference
类中
T
SoftReference
<
String
>
wrf
=
new
SoftReference
<
String
>
(
new
String
(
"str"
));
可用场景:
创建缓存的时候,创建的对象放进缓存中,当内存不足时,
JVM
就会回收早先创建
的对象。
弱引用
弱引用就是只要
JVM
垃圾回收器发现了它,就会将之回收,使用方式:
WeakReference
<
String
>
wrf
=
new
WeakReference
<
String
>
(
str
);
可用场景:
Java
源码中的
java.util.WeakHashMap
中的
key
就是使用弱引用,我的理解就是,
一旦我不需要某个引用,
JVM
会自动帮我处理它,这样我就不需要做其它操作。
虚引用
虚引用的回收机制跟弱引用差不多,但是它被回收之前,会被放入
ReferenceQueue
中。注意
哦,其它引用是被
JVM
回收后才被传入
ReferenceQueue
中的。由于这个机制,所以虚引用大多
被用于引用销毁前的处理工作。还有就是,虚引用创建的时候,必须带有
ReferenceQueue
,
使用例子:
PhantomReference
<
String
>
prf
=
new
PhantomReference
<
String
>
(
new
String
(
"str"
),
new
ReferenceQueue
<>
());
可用场景:
对象销毁前的一些操作,比如说资源释放等。
Object.finalize()
虽然也可以做这
类动作,但是这个方式即不安全又低效
上诉所说的几类引用,都是指对象本身的引用,而不是指
Reference
的四个子类的引用
(SoftReference
等
)
。
15、 泛型常用特点
泛型是
Java SE 1.5
之后的特性,
《
Java
核心技术》中对泛型的定义是:
“
泛型
”
,顾名思义,
“
泛指的类型
”
。我们提供了泛指的概念,但具体执行的时候却可以有具体的规则
来约束,比如我们用的非常多的
ArrayList
就是个泛型类,
ArrayList
作为集合可以存放各种元素,如
Integer, String
,自定义的各种类型等,但在我们使用的时候通过具体的规则来约束,如我们可以约
束集合中只存放
Integer
类型的元素,如
List
<
Integer
>
iniData
=
new
ArrayList
<>
()
使用泛型的好处?
以集合来举例,使用泛型的好处是我们不必因为添加元素类型的不同而定义不同类型的集合,如整
型集合类,浮点型集合类,字符串集合类,我们可以定义一个集合来存放整型、浮点型,字符串型
数据,而这并不是最重要的,因为我们只要把底层存储设置了
Object
即可,添加的数据全部都可向
上转型为
Object
。 更重要的是我们可以通过规则按照自己的想法控制存储的数据类型。
16、Java创建对象有几种方式?
java
中提供了以下四种创建对象的方式
:
new
创建新对象
通过反射机制
采用
clone
机制
通过序列化机制
17、有没有可能两个不相等的对象有相同的hashcode
有可能
.
在产生
hash
冲突时
,
两个不相等的对象就会有相同的
hashcode
值
.
当
hash
冲突产生时
,
一般
有以下几种方式来处理
:
拉链法
:
每个哈希表节点都有一个
next
指针
,
多个哈希表节点可以用
next
指针构成一个单向链
表,被分配到同一个索引上的多个节点可以用这个单向链表进行存储
.
开放定址法
:
一旦发生了冲突
,
就去寻找下一个空的散列地址
,
只要散列表足够大
,
空的散列地址总
能找到
,
并将记录存入
List
<
Integer
>
iniData
=
new
ArrayList
<>
()
阿里内部资料
再哈希
:
又叫双哈希法
,
有多个不同的
Hash
函数
.
当发生冲突时
,
使用第二个
,
第三个
….
等哈希函数
计算地址
,
直到无冲突
.
18、深拷贝和浅拷贝的区别是什么?
浅拷贝
:
被复制对象的所有变量都含有与原来的对象相同的值
,
而所有的对其他对象的引用仍然指
向原来的对象
.
换言之
,
浅拷贝仅仅复制所考虑的对象
,
而不复制它所引用的对象
.
深拷贝
:
被复制对象的所有变量都含有与原来的对象相同的值
.
而那些引用其他对象的变量将指向
被复制过的新对象
.
而不再是原有的那些被引用的对象
.
换言之
.
深拷贝把要复制的对象所引用的
对象都复制了一遍
.
19、fifinal有哪些用法?
fifinal
也是很多面试喜欢问的地方
,
但我觉得这个问题很无聊
,
通常能回答下以下
5
点就不错了
:
被
fifinal
修饰的类不可以被继承
被
fifinal
修饰的方法不可以被重写
被
fifinal
修饰的变量不可以被改变
.
如果修饰引用
,
那么表示引用不可变
,
引用指向的内容可变
.
被
fifinal
修饰的方法
,JVM
会尝试将其内联
,
以提高运行效率
被
fifinal
修饰的常量
,
在编译阶段会存入常量池中
.
除此之外
,
编译器对
fifinal
域要遵守的两个重排序规则更好
:
在构造函数内对一个
fifinal
域的写入
,
与随后把这个被构造对象的引用赋值给一个引用变量
,
这两个操作
之间不能重排序 初次读一个包含
fifinal
域的对象的引用
,
与随后初次读这个
fifinal
域
,
这两个操作之间不
能重排序
.
20、static都有哪些用法?
所有的人都知道
static
关键字这两个基本的用法
:
静态变量和静态方法
.
也就是被
static
所修饰的变量
/
方法都属于类的静态资源
,
类实例所共享
.
除了静态变量和静态方法之外
,static
也用于静态块
,
多用于初始化操作
:
public
calss PreCache
{
static
{
//
执行相关操作
}
}
此外
static
也多用于修饰内部类
,
此时称之为静态内部类
.
最后一种用法就是静态导包
,
即
import static
.import static
是在
JDK 1.5
之后引入的新特性
,
可以用
来指定导入某个类中的静态资源
,
并且不需要使用类名
,
可以直接使用资源名
,
比如
:
import static
java
.
lang
.
Math
.
*
;
public class
Test
{
public static
void
main
(
String
[]
args
){
//System.out.println(Math.sin(20));
传统做法
System
.
out
.
println
(
sin
(
20
));
}
}
21、3*0.1 == 0.3返回值是什么
false,
因为有些浮点数不能完全精确的表示出来
.