一、Java中的作用域有哪些?
成员变量的4种作用域对比
作用域与可见性 | 当前类 | 同一package | 子类 | 其他package |
---|---|---|---|---|
public | √ | √ | √ | √ |
private | √ | × | × | × |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
二、ArrayList和Vector的区别
- Vector的方法都是同步的,是线程安全的。而ArrayList的方法是线程不安全的,不是同步的。由于线程的同步必然要影响性能,因此,ArrayList一般多用于单线程环境下,而Vector一般多用于多线程环境下。
以add方法为例,源码如下
ArrayList add 方法源码
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
Vector add方法源码
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
- 当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小。ArrayList、Vector默认容量都是10。如果预先能了解到有多少数据,应该给ArrayList或Vector一个初始值,以减少集合自动扩容的次数,提高程序性能。
//指定集合初始值
ArrayList<String> list = new ArrayList<>(100);
三、HashMap、Hashtable、ConcurrentHashMap的原理与区别
HashTable
- 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化
- 初始size为11,扩容:newsize = olesize*2+1
- 计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length
HashMap
- 底层数组+链表实现,可以存储null键和null值,线程不安全
- 初始size为16,扩容:newsize = oldsize*2,size一定为2的n次幂
- 扩容针对整个Map,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入
- 当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀
- 计算index方法:index = hash & (tab.length – 1)
ConcurrentHashMap
- 底层采用分段的数组+链表实现,线程安全
- 通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍
- Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
Hashtable和HashMap都实现了Map接口,但是Hashtable的实现是基于Dictionary抽象类的。Java5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好
四、Java集合框架图
黄色的代表接口,绿色的是抽象类,蓝色的具体类
Collection Map
五、Java的八种基本数据类型
Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
1. 四种整数类型(byte、short、int、long)
- byte:8 位,用于表示最小数据单位,如文件中数据,-128~127
- short:16 位,很少用,-32768 ~ 32767
- int:32 位、最常用,-231-1~231 (21 亿)
- long:64 位、次常用
注意事项: int i=5; // 5 叫直接量(或字面量),即 直接写出的常数。 整数字面量默认都为 int 类型,所以在定义的 long 型数据后面加 L或 l。 小于 32 位数的变量,都按 int 结果计算。
2. 两种浮点数类型(float、double)
- float:32 位,后缀 F 或 f,1 位符号位,8 位指数,23 位有效尾数
- double:64 位,最常用,后缀 D 或 d,1 位符号位,11 位指数,52 位有效尾数
注意事项:浮点数字面量默认都为 double 类型,所以在定义的 float 型数据后面加F 或 f;double 类型可不写后缀,但在小数计算中一定要写 D 或 X.X 。浮点数是不精确的,不能对浮点数进行精确比较。
3. 一种字符类型(char)
- char:16 位,是整数类型,用单引号括起来的 1 个字符(可以是一个中文字符),使用 Unicode 码代表字符,0~2^16-1(65535)
注意事项:不能为 0个字符。
转义字符:\n 换行 \r 回车
两字符 char 中间用“+”连接,内部先把字符转成 int 类型,再进行加法运算,char 本质就是个数!二进制的,显示的时候,经过“处理”显示为字符。
4. 一种布尔类型(boolean)
true 真 和 false 假
类型转换:char--> 自动转换:byte-->short-->int-->long-->float-->double
强制转换:①会损失精度,产生误差,小数点以后的数字全部舍弃。②容易超过取值范围
记忆:8位:Byte(字节型) 16位:short(短整型)、char(字符型) 32位:int(整型)、float(单精度型/浮点型) 64位:long(长整型)、double(双精度型) 最后一个:boolean(布尔类型
代码测试
long n = 4; // 编译通过,可以不加L或l,4为int可以用long来装
long n2 = 4L; // n == n2 为true
float f = 2.4; // 编译不通过,2.4要加f 因为浮点数字面量默认都为double类型
float f2 = 2.4f; // 编译通过
double d = 2.4; // d == f2 为false 因为一个为float一个为double
double d2 = 2.4f; // 编译通过,2.4f为float 可以用double来装
// f2 == d2 为true
char a ='a';
char b = 'b';
char c = '陈'; // 编译通过
char d = 'a'+'b'; // A
char e = a+b; // 编译不通过
char f = 'ab'; // 编译不通过
char g = ''; // 编译不通过
六、Java中异常处理机制
1. Java异常
异常是什么?不正常的事件。运行程序时,它没有按照我们的预期执行,也就是不正常的运作,就叫异常。Java标准库内建了一些通用的异常。Throwable派生Error、Exception。Error类及其子类代表的是JVM本身的错误,并非程序的问题,不能由程序员通过代码进行处理。Exception及其子类代表的是在程序运行过程中产生的不期望发生的异常事件,可以被Java异常处理机制进行处理,是异常处理的核心。
2. 异常处理语句及语法
在写代码处理异常的时候,有两种不同的处理方式:
- 1.使用try..catch..finally处理
- 2.使用throws声明抛给函数调用者去处理
try...catch..finally语句块
try{
//1.存放可能发生异常的代码。
//2.如果没有发生异常,执行finally块代码(如果有finally块)并往下执行代码,否则,直接尝试去匹配catch块。
}catch(IOException e1){
//每一个catch块用于捕获并处理一个特定的异常,或者这异常类型的子类。
//catch后面的括号定义了异常类型和异常参数(局部的)。如果异常与之匹配且是最先匹配到的,则虚拟机将使用这个catch块来处理异常。
//在catch块中可以使用这个块的异常参数来获取异常的相关信息(getMessage()、printStackTrace()、getCause())。
}catch(Exception e2){
//...
}finally{
//finally块可要可不要。
//一个try至少要有一个catch块,否则, 至少要有1个finally块。但是finally不是用来处理异常的,finally不会捕获异常。
//finally主要做一些清理工作,如流的关闭,数据库连接的关闭等。
}
需要注意的是:
1.try块发生异常,那么try块中发生异常的那一行以下的代码都不会执行。
2.无论异常发生与否,异常是否有catch匹配处理,finally块都会执行。
3.如果catch中有return,先将return值暂存起来,再执行finally,最后再return
public class Client {
public static void main(String[] args) {
System.out.println("result:"+fun());
}
static int fun() {
int a = 1;
try {
a =2;
int b = 1/0;
System.out.println(" a = "+a); //异常后面的代码不会执行
return a; //异常后面的代码不会执行
}catch(Exception e){
a =3;
System.out.println("catch a = "+a);
return a; //将a的值暂存起来,return 的时候返回出去, 再执行finally,最后执行return
}finally {
a = 4;
System.out.println("finally a = "+a);
}
}
}
运行结果:
catch a = 3
finally a = 4
result:3
public class Client {
public static void main(String[] args) {
System.out.println("result:"+fun2());
}
static int fun2() {
int a = 1;
try {
a =2;
System.out.println(" a = "+a);
return a;//将a的值暂存起来,return 的时候返回出去, 再执行finally,最后执行return
}catch(Exception e){
//因为没有异常,所以下面代码都不会执行
a =3;
System.out.println("catch a = "+a);
return a;
}finally {
a = 4;
System.out.println("finally a = "+a);
}
}
}
运行结果:
a = 2
finally a = 4
result:2
3. 非检查异常 & 检查异常
非检查异常(unchecked exception):
包括Error和RuntimeException以及他们的子类。java编译器在编译时,不会提示和发现这样的异常,不要求在程序预处理这些异常,但是如果有异常产生,则异常将由JVM进行处理。当然如果你觉得可能会有异常就可以使用try..catch处理或throws抛出。
检查异常(checked exception):
包括除了Error和RuntimeException的其他异常。java编译器强制要求程序员预处理,不然编译器就会报错不让你通过。
4. Java自定义异常,应该继承Exception还是Runtime Exception
RuntimeException属于Exception的子类
Exception是在编译时候如果有异常就可以检查出来,比较严谨!
RuntimeException比较特殊,他及其子类对象,属于运行时候检查的异常,如果语法没有错误他不会在编译时候报异常的,只有运行的时候才会抛出异常!
至于继承谁,得看类具体的功能,其实这两点已经说的很明确了,如果继承了Exception要么抛出去给上级调用者,要么调用异常代码的时候进行捕捉,有相对应的处理方式。如果继承的是RuntimeException,可以不用抛,也可以不用捕捉但是问题是在运行的过程中才会展现出来,一但出错,后面程序将无法继续运行。
七、String,StringBuilder,StringBuffer三者的区别
- String : 字符串常量,不可变,非线程安全;适合声明一个字符串常量,不适合做连接字符串操作。
- StringBuilder:字符串变量,可变,非线程安全;适用于单线程下在字符缓冲区进行大量操作的情况。
- StringBuffer:字符串变量,可变,线程安全;适用多线程下在字符缓冲区进行大量操作的情况。
八、Class.forName的作用以及为什么要用它
我们查看Class.forName源码,可看到注释为:
Returns the {@code Class} object associated with the class or
* interface with the given string name
大概意思是:使用Class类中静态forName()方法获得与字符串对应的Class对象
Class.forName作用为:给定一个字符串变量,它代表一个类的包名和类名,通过Class.forName实例化它
//方式一
A a = (A)Class.forName("pacage.A").newInstance();
//方式二
A a = new A();
方式一和方式二效果是一样的。
在初始化一个类,生成一个实例的时候,newInstance()方法和new关键字除了一个是方法,一个是关键字外,它们的区别在于创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新类。从JVM的角度看,我们使用关键字new创建一个类的时候,这个类可以没有被加载。但是使用newInstance()方法的时候,就必须保证:
1、这个类已经加载;
2、这个类已经连接了。
而完成上面两个步骤的正是Class的静态方法forName()所完成的,这个静态方法调用了启动类加载器,即加载 java API的那个加载器。
现在可以看出,newInstance()实际上是把new这个方式分解为两步,即首先调用Class.forName方法加载某个类,然后实例化。 这样分步的好处是显而易见的。我们可以在调用Class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。Java中工厂模式经常使用newInstance()方法来创建对象
new关键字和newInstance()方法的区别:
newInstance: 弱类型,低效率,只能调用无参构造。
new:强类型,相对高效,能调用任何public构造。
九、什么是序列化?序列化的作用,应用场景
序列化:所谓java对象序列化就是将对象的状态转换成字节流,以后可以通过这些值再生成相同状态的对象,Java通过实现Serializable接口来实现序列化。
序列化作用:序列化将对象流化,然后进行传输和存储。再通过反序列化将流重组成对象。
应用场景:对象序列化后存在在文件中;对象经过分布式系统进行网络传输;
十、Collection和Collections有什么区别
- Collection是集合类的上级接口,继承与他的接口主要有Set 和List
- Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作
十一、int和Integer有什么区别
int是java提供的8种原始数据类型之一,Java为每个原始类型提供了封装类,Integer是java为int提供的封装类。
int的默认值为0,而Integer的默认值为null,即Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况。要想表达出没有参加考试和考试成绩为0的区别,则只能使用Integer。
在Hibernate中,如果将OID定义为Integer类型,那么Hibernate就可以根据其值是否为null而判断一个对象是否是临时的,如果将OID定义为了int类型,还需要在hbm映射文件中设置其unsaved-value属性为0。
另外,Integer提供了多个与整数相关的操作方法,例如,将一个字符串转换成整数,Integer中还定义了表示整数的最大值和最小值的常量。
十二、是否可以从一个static方法内部发出对非static方法的调用
不可以。因为非static方法是要与对象关联在一起的,必须创建一个对象后,才可以在该对象上进行方法调用,而static方法调用时不需要创建对象,可以直接调用。也就是说,当一个static方法被调用时,可能还没有创建任何实例对象,如果从一个static方法中发出对非static方法的调用,那个非static方法是关联到哪个对象上的呢?这个逻辑无法成立,所以,一个static方法内部发出对非static方法的调用。
十三、创建对象的几种方式
new、反射、反序列化、Clone
网友评论