java简答面试题汇总
- 第一章:java语言概述
- 1.1 java语言的特点是什么?
- 1.2 什么是跨平台性?原理是什么?
- 1.3 为什么要设置path(或者说,设置path的目的是什么)?(现在的JDK可以不配置环境变量也能使用)
- 1.4 分别简述什么是JDK、JRE、JVM,以及他们之间的关系。
- 1.5 .Java代码是如何运行的?
- 1.8 java中的注释有几种,分别怎么写?
- 1.9 什么是GC?GC的基本原理是什么?GC可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
- 第二章 基础语法(上)
- 2.1 java中标识符的命名规则与规范是什么?
- 2.2 什么是驼峰命名法?
- 2.3 简述八种基本数据类型以及其所占的字节数。
- 2.4 基本数据类型有哪几类?包含String吗?
- 2.5 char类型能否存储汉字,为什么?能不能直接存储数字,使用时是不是直接使用数字,为什么?
- 2.6 隐式转换和显示转换的区别?
- 2.7基本类型运算规则
- 2.8 & 和&& 的区别
- 第二章 基础语法(下)
- 2.1 switch表达式都可以使用什么数据类型?
- 2.2 分支结构有几种?语法结构怎么写?有什么区别?
- 2.3 .循环结构有几种?语法结构怎么写?有什么区别?
- 2.4 怎么结束多层循环?
- 2.5 生成随机数的方式有几种?语法结构怎么写?有什么区别?
- 2.6 数组复制的方式有几种?怎么写?有什么区别?
- 2.7 什么是二维数组?怎么创建二维数组?
- 2.8 怎么定义一个方法?为什么要定义方法?
- 2.9 什么是可变参数?
- 2.10 double 、Double 、BigDecimal的区别
- 第三章 面向对象
- 成员变量和局部变量的区别?
- 3.2 静态变量和实例变量的区别?
- 3.3 重载与重写区别
- 3.4 简述final关键字的用法
- 3.5 this和super的区别
- 3.6 为什么this()或super()只能放在方法的第一行?为什么二者不能同时出现?
- 3.7 .说一下static关键字的用法和特点
- 3.8 静态方法能不能继承?能不能重写?
- 3.9 简述抽象类和接口的区别
- 3.10 实现多态的前提
- 3.11 什么是匿名内部类 ,什么是lambda表达式? 二者有什么区别和联系?
- 第四章 API(application programming inteface)
- 4.1 ==和equests在做比较时的区别?
- 4.2 String、StringBuilder、StringBuffer的区别?(面试题)
- 4.3 什么是自动拆装箱?
- 4.4 什么是javaBean规范?
- 4.5 Date 和 Calendar的区别
- 4.6 Date 和 Calender之间的相互转换
- IO简答
- 什么是输入流?什么是输出流?
- 3.什么是字节流?什么是字符流?
- 4.什么是高级流?什么是低级流?
- 5.什么是缓冲流?
- 6.什么是对象流?
- 7 .什么是序列化?什么是反序列化?
- 8.RandomAccessFile 与 FileOuputStream的异同?
- 4.6 异常
- 1. java中的异常处理机制有几种?怎么实现?
- 2. 简述java中的异常处理机制?
- 3. final 、finally 、finalize三个关键字的区别?
- 4. Error和Exception的区别
- 5.RuntimeException和CheckedException的区别
- 6. throw 和 throws的区别?
- 7.什么是GC? GC的工作原理是什么?有什么方法可以调用GC?
- 8. 说出9个常见的异常。
- 9.如何自定义异常类?
- 4.7线程1
- 1.进程和线程的区别
- 2、并发和并行的区别
- 3.同步运行和异步运行的区别
- 4.简述线程的生命周期
- 5.创建线程的方式有几种?区别是什么?
- 6.启动线程调用什么方法?可不可以直接调用run方法?为什么?
- 7.sleep和wait的区别?
- 4.8 线程2
- 1.简述线程池的工作原理
- 2.什么是守护线程?它与普通线程有什么区别?
- 3.synchronized关键字的用法
- 4.什么是单例模式?
- 5.单例设计模式对象创建方式:懒汉式和饿汉式有什么区别?如何实现?
- 6.如何实现多线程并发安全?
- 4.9 集合
- 1.怎么将集合转换为数组?怎么将数组转换为集合?
- 2.什么是迭代器?如何使用?
- 3.什么是泛型?作用是什么?泛型的用处有哪些?
- 4.迭代器、新循环、foreach在遍历集合时有何不同?
- 5.如何对List集合进行排序?
- 4.10集合2
- 1.请描述Array和ArrayList有何区别?什么时候更适合用Array?
- 2.ArrayList,LinkedList和Vector的区别
- 3.简述ArrayList的扩容原理
- 4.LinkedList如何进行元素的查找
- 5.什么是队列?什么是双端队列?什么是栈?存取元素时有什么特点?
- 6.遍历Map的方式
- 4.10集合3
- 1.HashMap扩容原理
- 2.HashMap底层结构
- 3.HashSet存储原理
- 4.List和Set的区别
- 5.HashMap和Hashtable的区别
- 6.为什么HashMap查询速度快?
- 五、泛型
- 5.1.什么是泛型?作用是什么?都可以用在什么地方?
- XML(可扩展标记语言)
- xml文件的语法要求有哪些?
- 7.常用的xml解析方式
- 8.什么是maven
- 反射
- 1.什么是反射
- 2.反射里获取类对象方式有三种:
- 3.使用反射的作用:
- 注解
- 1..注解的分类,你用过哪些注解,它们的作用是什么?
- 11.谈谈你对socket的理解
- 单例设计模式
- 1.什么是单例设计模式?
- 2. 单例设计模式的开发步骤
- 3.常用的实现方式:
第一章:java语言概述
1.1 java语言的特点是什么?
面向对象性: 两个基本概念:类、对象;三大特性:封装、继承、多态
健壮性: 吸收了C/C++语言的优点,但去掉了其影响程序健壮性的部分(如指针、内存的申请与释放等),提供了一个相对安全的内存管理和访问机制
跨平台性: 通过Java语言编写的应用程序在不同的系统平台上都可以运行。“Write once , Run Anywhere”
简单性 ,高性能
编译性 , 解释性
分布式处理
开源
1.2 什么是跨平台性?原理是什么?
答: 通过Java语言编写的应用程序在不同的系统平台上都可以运行,
原理:在需要运行java应用程序的操作系统上,先安装一个Java虚拟机(JVM Java Virtual Machine)即可。由JVM来负责Java程序在该系统中的运行。
因为有了JVM,所以同一个Java程序在不同的操作系统中都可以执行。这样就实现了Java程序的跨平台性。也称为Java具有良好的可移植性。
1.3 为什么要设置path(或者说,设置path的目的是什么)?(现在的JDK可以不配置环境变量也能使用)
答: 目的是为了在控制台的任何文件路径下,都可以调用jdk指定目录下的所有指令。
1.4 分别简述什么是JDK、JRE、JVM,以及他们之间的关系。
答:
JDK:Java Development Kit是针对Java开发员的产品,是整个Java的核心,包括了Java运行环境JRE、Java开发工具集(jdk\bin)和Java基础类库(jdk\jre\lib\rt.jar)。
JRE:Java Runtime Environment是运行JAVA的运行时环境,包含JVM和Java核心类库。
JVM:Java Virtual Machine,Java虚拟机的缩写,是整个java实现跨平台的最核心的部分,能够运行以Java语言写的软件程序。
1.5 .Java代码是如何运行的?
答:java代码编写完成后,通过编译器先将(.java)源文件编译成(.class)字节码文件,然后通过JVM将字节码文件编译成对应操作系统的指令集。
编译:代码编写完成后,通过编译(命令行采用javac命令)将java源文件即.java文件转换为字节码文件 即 .class文件。
运行:然后先启动java虚拟机,不同的操作系统有不同的JVM,因此java跨平台。然后在指定的操作系统上,加载字节码文件。字节码文件都一致,但每个JVM不同,每个JVM都是根据服务器硬件而专门编写的。这样JVM虚拟机会把字节码文件再次编译成对应硬件服务器上的指令集。从而实现java代码的跨平台运行。
1.8 java中的注释有几种,分别怎么写?
答:3种:
//单行注释
/* 多行注释 /
/* 文档注释 */
注释的作用: 代码的解释说明。不影响程序的运行,用于辅助读程序。
1.9 什么是GC?GC的基本原理是什么?GC可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
答:
1.GC是garbage Collection 的缩写,是垃圾回收器.
2.GC的工作原理:当GC确定一些对象不再有任何引用指向该对象时,由对象的GC回收这些内存空间。
3.可以。
4.程序员可以手动执行System.gc(),通知GC运行,但是并不保证GC一定会执行。
第二章 基础语法(上)
2.1 java中标识符的命名规则与规范是什么?
答:
①可以由26个大小写英文字母、数字0-9,下划线_,美元符号$符组成,不能包含@ % 空格等其他特殊字符。
②标识符不能以数字开头,可以以字母,下划线或美元符号开头
③要见名知意,标识符不能是关键字
④严格区分大小写
⑤标识符命名规范遵守驼峰命名规则,变量名首字母小写,多个单词从第二个单词开始首字母大写,通常方法名和变量名命名规范相同,类名首字母要大写,包名全部采用小写
2.2 什么是驼峰命名法?
答:命名规则,变量首字母小写,多个单词组成从第二个单词开始首字母大写
2.3 简述八种基本数据类型以及其所占的字节数。
位bit 字节byte 1byte=8bit
2.4 基本数据类型有哪几类?包含String吗?
答:基本数据类型包括byte、int、char、long、float、double、boolean和short。
java.lang.String是java中定义的一个类,类都属于引用数据类型。
2.5 char类型能否存储汉字,为什么?能不能直接存储数字,使用时是不是直接使用数字,为什么?
答:能存储汉字。因为java采用 Unicode编码,一个字符占2字节,汉字占一个字符。而char在java中占两个字节,所以可以存储中文
能直接存储数字,无论是数字,字母还是汉字或者其他的内容都可以存储。
使用时不是直接使用数字,给char变量赋值数字,运行时会自动转换为对应ASCll的字符。0-127是对应的ASCll表中值
char类型的标志:单引号’ ',单引号中有且只能有一个字符,至少有一个字符,为空编译报错。不能为空。
2.6 隐式转换和显示转换的区别?
答: 小类型转换大类型时会发生隐式转换又叫自动转换,编译器自动转换
大类型转换小类型时叫显示转换又叫强制转换,需要我们在转换的变量或字面值前加小括号,小括号中放入要转换成的数据类型。需要注意的是,如果小数转成整数,会直接将小数点后的所有位数全舍弃,不会四舍五入。
2.7基本类型运算规则
1.不同数据类型进行运算,结果类型与最大数据类型一致
2.byte,short,char三种比int小的整数,在计算时会自动转成int
3.整数运算溢出:整数运算,超过最大取值范围时,再转会回到最小
System.out.println(300000000606024365);负数的最大值
System.out.println(300000000l606024365);正最大值
4.浮点型运算不精确(API阶段解决)
5.浮点数的特殊值
System.out.println(3.3/0);//无穷大 System.out.println(0/0.0);//NaN
2.8 & 和&& 的区别
答:① & ,既是按位与 又是逻辑与,当 & 两边 的表达式是数值时,则此时是按位与,会将其转换成二进制进行按位运算,当 & 两边是boolean类型时,则此时是逻辑与,任意一边表达式的结果为false,则返回结果为false。
② && 逻辑与,俗称短路与,左右两边表达式结果为boolean类型。
二者区别: & 要将左右两边的表达式都判断完才返回判断结果。&& 只要有一个表达式为false,则返回结果,若左边表达式结果为fasle,则右边表达式不再进行判断操作。
第二章 基础语法(下)
2.1 switch表达式都可以使用什么数据类型?
答:byte short char int enum(枚举类型jdk5.0新增),String(jdk7.0新增)
2.2 分支结构有几种?语法结构怎么写?有什么区别?
4种,分别为单分支,双分支,多分支, switch-case。
单分支:if(boolean表达式){ 语句;}
双分支:if( boolean表达式 ){
满足条件执行;
}else{
不满足条件执行;
}多分支:if(boolean 表达式1){
语句1;
}else if(boolean 表达式2){
语句2;
}else if()……switch (变量){
case 常量1:[语句1];[break];
case 常量2:[语句2];[break];
case 常量3:[语句2];[break];
[default:语句;]
}
2.3 .循环结构有几种?语法结构怎么写?有什么区别?
循环结构是指在程序中需要反复执行某个功能而设置的一种程序结构。
它由循环体中的条件,判断继续执行某个功能还是退出循环。
根据判断条件,循环结构又可细分为先判断后执行的循环结构和先执行后判断的循环结构。
三种:
for (初始化条件; boolean类型条件;迭代条件){
循环体;
}
初始条件只初始化一次,从第二次开始只判断是否满足循环条件执行while(循环条件){循环体;},满足条件执行
do{
循环体;
} while(boolean 类型的循环条件);满足循环条件执行区别:for循环和while循环的初始化条件部分的作用范围不同。for() 和 while()可以相互转换,do - while()至少执行一次。
2.4 怎么结束多层循环?
答: 结束多层循环使用:
①标记break;循环后的代码仍然执行。标记名任意,冒号必须有。格式为: break 标记名;例如:
outer: for(; ; ){
for( ; ; ){
break outer;
}
}
②return ;return 是结束整个程序,return语句后面直接跟分号 ,不能跟其他语句。使用return语句结束循环,循环后的所有代码不再执行
结束循环可以使用:
continue:结束本次循环,继续下一次循环。
break :结束当前循环,如果是多层循环,只是跳出本层循环。
return; 结束整个程序
2.5 生成随机数的方式有几种?语法结构怎么写?有什么区别?
两种:
一:Math.random(),产生的是double 类型的随机数,直接调用该方法产生的是0.0-1.0的double 类型的数,想要其他区间的数可以写成: Math.random() * 范围数,该方式产生的是 [ 0 - 范围数),左闭右开。想要包括范围数,可以在后面 + 1操作。
二:Random类对象。如果需要产生多个随机数,可以声明Random对象,语法格式为:Random random = new Random();想要生成指定区间[a,b)的数。使用规则如:
int num = random.nextInt() * b + a;
2.6 数组复制的方式有几种?怎么写?有什么区别?
答:3种;
第一种,数组工具类:Arrays.copyOf(数组,新的长度),用一个已存在的数组进行拷贝给新数组,存在扩容和缩容。
Arrays.copyOf(T[] orginalArr,int newLength);第一个参数是原数组,第二个参数是新数组的长度第二种:System.arraycopy(orginalArr, int srcposition, destinctionArr , int length);
arraycopy(原数组名, 从原数组指定位置开始复制, 目标数组, 复制到目标数组指定位置, 复制长度为目标数组指定长度);第三种:数组的浅层复制: arr1 将自己保存的数组在堆中的地址值给到arr6,两个变量(引用)指定的是堆中的同一个数组,并没有创建新数组。所以修改arr6中的元素,arr中的元素也会发生改变,因为二者作用于同一个数组对象。
2.7 什么是二维数组?怎么创建二维数组?
二维数组:一个一维数组的元素还是一个一维数组,称为二维数组
静态创建:
数据类型[ ][ ] 数组名 = {{},…};动态创建:
数据类型[ ][ ] 数组名 = new 数组名[维度][ 可选维度];没有指定第一维度时,编译报错,没有指定维度输出会报空指针异常。
2.8 怎么定义一个方法?为什么要定义方法?
修饰符 返回值类型 方法名(参数列表){方法体;}
定义方法的作用:提高代码的复用性,安全性
2.9 什么是可变参数?
可变参数,是jdk 5.0开始支持参数列表中用连续的三个点表示:
语法格式:数据类型 …形参变量名
可变参数只要类型匹配,个数不限。
当参数列表有多个参数时,可变参数必须放在参数列表的最后。否则编译报错
可变参数本质上是数组,可变参数参数名相当于数组名,想要遍历可变参数时,采用一维数组遍历方式
2.10 double 、Double 、BigDecimal的区别
double 是基本数据类型,浮点型,占8字节,是小数类型的字面值。默认值为0.0。基本类型没有方法 和属性。
Double 是double 对应的包装类,可以创建对象和调用方法。
BigDecimal用于解决double 或 Double运算精度丢失问题,BidDecimal本身是一类。推荐使用String类型的形参构造对象。不会出现精度丢失问题
第三章 面向对象
成员变量和局部变量的区别?
①声明的位置不同,成员变量声明在类体中方法外,局部变量声明在类体方法中,或代码块中。
②成员变量有默认值,局部变量没有默认值,局部变量使用之前必须初始化(必须手动赋值才能使用)否则编译报错
③作用域不同。成员变量作用域是整个所在类。局部变量作用域是所在方法或代码块中
④当成员变量和局部变量同名时,变量的使用遵循就近原则。
3.2 静态变量和实例变量的区别?
静态变量:① 在类中方法外使用static修饰的变量为静态变量(局部变量不能用修饰符修饰),属于类的资源,跟类一起加载,可以直接使用类名调用,或使用对象调用(不推荐),全局共享,只有一份;编译期加载进内存。
实例变量:在类中方法外没有static修饰的变量为实例变量,实例变量属于对象的资源,只有创建完对象才能使用,需要用对象调用。每个对象都有一套特有的实例变量,运行时加载进内存。
相同点:二者都有默认值
3.3 重载与重写区别
重载:同一个类中,方法名相同,参数列表不同的方法称为方法的重载;
遵从两同一不同的规则:两同: 同一个类,方法名相同。
一不同:参数列表不同;
注意: 参数列表不同指的是:参数个数,形参顺序,形参类型不同
重载与与返回值类型,访问权限,形参变量名,方法体无关。重写:发生在父子类中,子类对父类已有的非私有方法重写。(构造方法不能被继承)
遵从两同两小一大原则:
两同:方法名相同,参数列表相同。
两小:①若父类返回值类型为基本数据类型或void,子类返回值类型必须与父类返回值类型保持一致。若父类返回值类型为引用类型,子类可以与父类相同,也可以为父类返回值类型的的子类。② 子类抛出的异常必须小于父类(若父类中没有抛出异常,子类只能使用try-catch捕获解决异常)。
一大:子类访问权限必须大于等于父类
3.4 简述final关键字的用法
①final修饰类不可被继承
②final修饰方法不能被重写
③final修饰变量称为常量,初始化之后不可修改。
拓展:
④final 与 static 共同修饰变量称为静态常量。必须声明并赋值。
⑤final不可与abstract一起使用。
3.5 this和super的区别
this 指代子类对象引用,使用super可以调用父类资源,( 因为此时可能没有创建父类对象,所以不能说super指向父类对象。)
②当本类成员变量名和局部变量名相同时,用this.来区分成员变量和局部变量。当父类成员变量与子类成员变量相同时,用super来区分父类成员变量和子类成员变量。拓展:
③可以使用super([参数列表])调用父类构造方法初始化父类属性。可以使用this([参数列表])初始化成员变量。
3.6 为什么this()或super()只能放在方法的第一行?为什么二者不能同时出现?
①因为java不允许调用没有初始化的成员。
②this()中显示或隐式调用super()初始化父类成员变量,所以二者不能同时使用,否则会造成两次初始化父类成员变量。
3.7 .说一下static关键字的用法和特点
答:static 可以修饰变量,修饰方法,修饰静态内部类,静态导入。
修饰变量称为类变量,修饰方法称为类方法,编译阶段加载进内存,与类一起加载。
static修饰静态内部类是随对象一起加载,即运行时加载。
3.8 静态方法能不能继承?能不能重写?
答:① 静态方法可以被继承,但是不能被重写。当子类中的静态方法与父类中的静态方法相同时,会隐藏父类静态方法。
②因为静态方法是编译期绑定的 ,方法重写是运行期绑定的。静态资源是类特有资源。
3.9 简述抽象类和接口的区别
①:相同点: 都可以有抽象方法,都不能被实例化(即是不能创建对象)(但是可以声明引用,多态的应用)。
不同点:
①定义不同,声明接口使用interface ,声明抽象类使用 abstract class
② 接口可以多继承,抽象类只能单继承(java中的类只能单继承)
③变量的区别:接口中只能有静态常量,抽象类中可以有普通变量。
④ 接口中没有构造方法,抽象类中有构造方法
⑤ 方法的区别:接口在JDK8之前只能有抽象方法(和静态常量) ,JDK8 之后可以有static 或 default修饰的普通方法,有方法体,即是有大括号{};抽象类中可以有抽象方法,也可以有普通方法
⑥ 接口中的访问权限都是public 的,抽象类中四种访问权限都可以,
⑦ 接口中可以简写(变量只写数据类型 变量名 = 初始化)(方法只写返回值类型 方法名(); ),接口中变量省略public static final 会自动拼接识别为静态常量, 接口中的方法可以省略public abstract会自动拼接识别为抽象方法,抽象类中不可以简写。
3.10 实现多态的前提
继承
重写: 成员变量和静态方法不存在重写现象,所以调用的都是父类的
向上造型: 父类引用指向子类对象:
3.11 什么是匿名内部类 ,什么是lambda表达式? 二者有什么区别和联系?
匿名内部类就是没有类名,声明在一个类中,只用一次,多用于接口和抽象类。正常情况下,接口和抽象类都不能new对象,但与匿名内部类结合使用时,可以new 接口名{类体;}; 或 new 抽象类名{类体};注意大括号后分号结尾。
lambda表达式 是 JDK8之后推出的一个新特性:是对匿名内部类的简写,但不完全等同于匿名内部类
语法:(参数列表) -> {方法体;}
lambda表达式创建时,必须是接口的实现类。而且实现的接口中只能有一个抽象方法。
使用lambda表达式:编译器会给程序的语义分析出实现的是哪个结构,由于要求接口中只能有一个抽象方法,所以也能知道重写后的方法是什么。最终会将它还原为匿名内部类。还可以简写省略参数类型:
当方法体中只有一句代码时,方法的方法体{}可以省略,并且如果该方法要求有返回值,则return关键字也要一起省略。区别:lambda表达式是匿名内部类的简写,但是lambda表达式的要求更多些。
联系:使代码更简洁,可以在定义一个类的同时对其进行实例化,
第四章 API(application programming inteface)
4.1 ==和equests在做比较时的区别?
答: == 既可以比较 基本数据类型,又可以比较引用类型。
当== 两边操作数是基本数据类型时,比较的是值本身。当 == 两边是引用类型时比较的是引用地址值
equals() 只能比较引用类型。Object中的equals()的作用和 == 作用相同,比较的都是引用类型的地址值,若想比较对象的内容,对象所在的类必须重写equals()方法。
4.2 String、StringBuilder、StringBuffer的区别?(面试题)
相同点: 三者都可以操作字符串。
String 是不可变字符串对象,每次修改字符串都会创建新对象
StringBuilder、StringBuffer 是可变字符串序列(对象),StringBuilder 和 StringBuffer 在修改字符串时不会创建新对象
当需要对字符串频繁的修改时,推荐使用StringBuilder 或 StringBuffer
StringBuffer是jkd 1.0 提供的。StringBuilder是jdk 1.5提供的,二者的常用方法名 和实现原理是一样的。区别在于StringBuffer是线程安全的,StringBuilder是线程不安全的。如果不涉及线程安全问题,推荐使用StringBuilder,因为它的效率会更高一些执行效率上: StringBuilder 快于 StringBuffer 快于 String
4.3 什么是自动拆装箱?
自动拆装箱是jdk5.0提供的
自动拆箱:将包装类自动装换为基本数据类型。
自动装箱:将基本数据类型转换成包装类。
自动拆装箱是编译器认可的,不是虚拟机认可的,在编译器编译代码时发现有包装类和基本类型之间的转换会自动添加转换代码,而不需要我们在源代码(.java源文件)中编码完成。
4.4 什么是javaBean规范?
5.JavaBean类必须 实现Serializable接口,提供序列化版本号
1.JavaBean类必须是一个公共类,即访问权限应设置为public修饰。
2.JavaBean类必须提供一个公共的无参构造方法
3.javaBean类不应该有公共属性,所有属性都应该是private修饰的。
4.为私有属性提供符合命名规范的get/set方法
4.5 Date 和 Calendar的区别
Date是日期类,Date 是通过调用方法来获取时间。
Calender 是日历类,是一个抽象类,不能实例化对象,把能用到的时间封装成静态常量,通过子类(GregorianCalendar)调用。
4.6 Date 和 Calender之间的相互转换
- Date getTime();返回一个表示此Calendar时间值的Date对象
- void setTime(Date date); 用当前的Calendar表示给定的Date
IO简答
什么是输入流?什么是输出流?
1.根据数据的流向分为:输入流 和 输出流 (参照点是当前程序。从程序写出到磁盘,从磁盘读入到当前程序)
输入流:从外界到程序,用来读取数据
输出流:从程序到外界,用来写出数据
3.什么是字节流?什么是字符流?
2.根据数据的操作单位分为:字节流,字符流
字节流:以字节为单位读写数据
字符流:以符节为单位读写数据
4.什么是高级流?什么是低级流?
根据是否直接操作数据分为:节点流(低级流) 和 处理流(高级流).高低级之分只是是否能直接作用于文件。
低级流:也叫节点流,负责实际读写数据
高级流(不能直接作用于文件):也叫处理流,不能独立存在,必须连接在其他流上,目的是当数据流经当前流时对其他某些加工处理, 简化我们的读写操作(例如缓冲流,当数据流经他时对数据起到缓存作用)。高级流读写数据一定是建立在低级流的基础上进行
5.什么是缓冲流?
缓冲流即是具有缓冲区的流,缓冲流有5种。
基于字节操作的有:BufferedInputStream 和 BufferedOutputStream ;
基于字符流操作的:BufferedWriter 和 BufferedReader(缓冲字符输出流和缓冲字符输入流)PrintWriter(自带刷新功能的缓冲字符输出流)
6.什么是对象流?
对象流有两种java.io.ObjectOutputStream 和 ObjectInputStream
(高级流,不能直接操作文件,操作的是对象)对象流是一个对字节流,也是一对高级流,
在流连接中的作用是进行对象的序列化与反序列化方便我们进行对java对象的读写操作 。
7 .什么是序列化?什么是反序列化?
当调用writeObject(Object obj)方法将一个对象写出时,首先该对象经过对象流,他会将对象按照其结构转换为一组字节,存储到文件中,这个过程称为对象的序列化。
实现序列化的对象所在类必须实现Serilizable接口调用readObject() , 将序列化后的对象从字节序列形式还原为java对象的过程。返回类型是Object类型,如果我们想要输出对象,需要强转
8.什么是持久化?
序列化后的字节在经过对象流连接的文件流写出,而文件流会将这个字节最终写出到文件中(磁盘)做长久保存,这个过程称为数据持久化。
8.RandomAccessFile 与 FileOuputStream的异同?
① RandomAccessFile是基于指针的随机读写形式,可以对文件任意位置进行局部覆盖,
② 文件流:由于java标准的IO是顺序读写形式,对于操作而言只能顺序先后写。不能回退,写数据时如果不指定追加模式为true,则每次写的数据会覆盖文件中原有数据,因此写操作的灵活性不如RandomAccessFile。
③ FileOuputStream可以作流连接,联合若干高级流完成复杂的写操作,RandomAccessFile不能流连接
4.6 异常
1. java中的异常处理机制有几种?怎么实现?
两种:
捕获并处理:try-catch-[finally]
try{
可能出现异常的代码片段;
}catch(XXXException e){
处理try中出现的XXXException的代码方案;
}
throws 异常类。 由调用者处理该异常,调用者也可以继续往外抛出该异常或者使用try-catch处理
2. 简述java中的异常处理机制?
三种:
try-catch-finally捕获并处理异常
try{
可能出现异常的代码片段;
}catch(XXXException e){
处理try中出现的XXXException的代码方案;
}
第二种:在方法声明上 使用 throws 抛出 异常类,可以抛出多个异常类
第三种: 在方法中: throw 异常类对象 。只能声明一个。
3. final 、finally 、finalize三个关键字的区别?
final是java关键字,可修饰属性,方法,类。
修饰属性初始化后不能别修改。
修饰方法可被继承不能别重写。
修饰类不能被继承。finally 是异常处理机制try - catch-finally方式中的一块,附有一个语句块,无论当try中是否出现异常,只要系统不出现中断情况,即System.exit(0)情况.,表示finally中的代码一定会被执行。通常将关闭流,释放资源,关闭数据库连接操作定义在finally中。
finalize()是方法名,Object类中定义,当垃圾回收其确定不存在当前对象的更多引用时,由该对象的垃圾回收器调用。
4. Error和Exception的区别
Error 是系统级错误,是Java运行环境内部错误或者硬件问题,不能通过程序来处理这样的问题,发生错误时程序退出运行。
它是Java虚拟机抛出的,比如虚拟机内存溢出等。Exception是程序需要处理的异常,由于程序设计的不完善(程序员逻辑问题)而出现的问题,程序必须进行处理,比如空指针异常,数组下标越界异常等。
通常程序中处理的异常都是Exception
5.RuntimeException和CheckedException的区别
CheckedException:编译时异常(检查异常)
java 语言强制要求处理的所有非运行时异常,例如IOException ,SQLExceptionRuntimeException: 运行时异常, 一般是由于程序逻辑错误引起的,比如算术异常,空指针异常等待。
6. throw 和 throws的区别?
1.throw 声明位置在方法体内,后面跟的是异常类的实例(new XXXException(”异常信息描述“)),只能有一个;
throws 声明位置在方法声明后,后面跟的是异常类名。
2.throw 后面只能抛出一个异常对象,throws同时抛出多个异常时中间用“,”逗号隔开。throw 和 throws 联系:
1.如果方法中有throw 抛出的 RuntimeException 及其子类对象,则方法声明上可以没有throws,(由该方法的调用者处理该异常),除此之外,方法声明上必须有throws
7.什么是GC? GC的工作原理是什么?有什么方法可以调用GC?
答:GC:垃圾回收器。
在JVM中,如果GC确定堆中的对象没有被更多的引用指向 ,就会被GC回收,用System.gc()调用。
8. 说出9个常见的异常。
①数字格式异常NumberFormatException
②算术异常ArithmeticException
③数组下标越界异常ArraysIndexOutOfBounds
④字符串下标越界异常StringIndexOutOfBounds
⑤空指针异常NullPointerException
⑥类型格式异常ClassFormatException
⑦文件找不到异常NotFoundFileException
⑧引用类型强制转换异常ClassCastException
⑨IllegalArgumentException非法参数异常
⑩NotSuchElementException不能找到元素异常,迭代器next获取元素时
11.ConCurrentModificationException 在迭代器遍历集合时不允许使用集合方法删除元素报的异常
9.如何自定义异常类?
定义自定义异常 需要注意以下几点:
1.类名定义时要做到见名知意
2.需要继承自Exception,直接或间接继承都可以
3.提供继承自Exception中提供的所有构造器
4.7线程1
1.进程和线程的区别
答:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位。一个进程可以包含多个线程
具体区别:
1.地址空间:进程有自己独立的地址空间,每启动一个进程,系统都会为其分配地址空间。
线程没有自己独立的空间,同一进程内的线程共享本进程的地址空间。
2.资源拥有:进程之间的资源是独立的,同一进程内的线程共享本进程的资源。(相当于同一时间下只能运行一个程序)
3.执行过程:每个独立的进程都有一个程序运行的入口,可以独立执行,但是线程不能独立执行,必须依附在应用程序中 ,由应用程序提供多个线程的执行控制。
2、并发和并行的区别
并发:是指一个处理器(单核)(采用时间片分配形式)同时处理多个任务
并行:是指多个处理器或者多核处理器同时处理多个不同的任务。
并发是逻辑上的同时发生。而并行是物理上的同时发生。
3.同步运行和异步运行的区别
同步运行: 多线程并发运行时,多个线程同时请求同一个资源,为了保证该资源数据安全,采用同步机制。因为有同步机制的限制,在当前线程使用该资源并且没有释放该资源时其他线程只能进入阻塞状态。直到当前线程释放资源,其他线程才能抢占该资源,同理,没有抢到该资源的线程都得进入阻塞状态等待唤醒。
异步运行:多个线程访问同一资源,无论是否有线程正在使用该资源,任意一个线程都可以随时抢占该资源,这一过程可能会导致线程死锁。
区别:同步保证了数据的安全性,异步不安全容易导致线程死锁。
异步执行效率高,同步执行效率低。
4.简述线程的生命周期
线程生命周期,总共有五种状态:
1、新建状态(New):当线程对象被创建后,即进入了新建状态,如:Thread t = new MyThread();
2、就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行(获得时间片),并不是说执行了t.start()此线程立即就会执行;
3、运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
4、阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态;
根据阻塞产生的原因不同,阻塞状态又可以分为三种:
等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5、 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
5.创建线程的方式有几种?区别是什么?
创建线程有四种方式:
第一种创建线程的方式:定义一个类直接继承Thread类重写Thread类中的run()方法。
优点:结构简单,便于匿名内部类形式创建。
缺点:
1.直接继承Thread类,会导致不能在继续继承其他的类去复用方法(因为java只支持类的单继承),这在实际开发中是非常不便的。
2.定义线程的同时重写了run方法,会导致线程与线程任务(run()方法内的执行代码)绑定在一起,不利于线程的重用。第二种创建线程的方式:定义一个类实现Runnable 接口单独定义线程的任务重写Runnable接口中的抽象方法run(),通过创建线程要执行的任务,创建执行任务的线程。
优点:实现接口的方式解决了类的单继承性的局限性。
实现的接口的方式更适合来处理多个线程共享数据的情况。
线程和任务分开,便于实现线程的重用第三种创建方式:定义一个类实现Callable接口,重写call()方法定义要执行的任务,
java.util.concurrent.Callable
在Java中创建线程最常用的方式是继承Thread类或者实现Runnable接口,这两种方式的的缺点是在任务完成后无法直接获取到执行结果(因为run()没有返回值),必须通过共享变量或线程间通信,使用起来很不方便。
于是从JDK5.0 开始提供了Callable接口,以及搭配使用的Future接口的实现类FutureTask类,通过使用它们可以在任务执行完毕后得到执行结果,并且抛出异常。java中为我们提供了一个Future接口的实现类 FutureTask第四种:通过newFixedThreadPool(int nThread)创建指定大小的线程池。当创建线程池时,传入的参数为最大线程数量,底层源码会判断传入的最大线程数量是否大于核心线程数量,
如果大于(不可能大于),会先创建核心线程数量。
创建线程是在ThreadPoolExecutor构造方法调用重载的构造方法的默认工厂创建,
通过execute(任务)传入任务时,如果任务数量小于线程池数量,会分配线程执行任务。
若任务大于线程池数量,超过的任务会被放在阻塞队列排队等待线程空下来。优点:任务执行完后Callable可以获取执行结果。
缺点:复杂。
区别:①Thread不可以使用lambda表达式创建。Callable和Runnable可以
②Thread 和 Runnable 没有返回值,不能抛异常,Callable有返回值可以抛异常
③直接继承Thread类,线程和线程任务绑定在一起,不利于线程的重用,Runnable 和 Callable将线程任务和线程分开。
6.启动线程调用什么方法?可不可以直接调用run方法?为什么?
启动线程时用start()方法,自动调用重写后的run()方法,不能直接调用run()方法,直接调用run()方法,程序会顺序执行(不启动线程),与线程无关,也无法实现并发运行。
7.sleep和wait的区别?
相同点:都能让线程进入阻塞状态。
不同点:
sleep(long ms)方法,是Thread类的静态方法。wait()方法,属于Object类中的。
调用 sleep()方法导致当前线程暂停执行指定的时间,但是依然保持监控状态不释放锁,当指定的时间到了又会自动恢复就绪状态,等待再次被分配时间片并发运行。
而调用wait()方法,线程会释放锁,进入等待此对象的等待锁定池,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。该线程才能重新获得对监视器的所有权后才能继续执行。
4.8 线程2
1.简述线程池的工作原理
工作原理:
当创建线程池时,传入的参数为最大线程数量,底层源码会判断传入的最大线程数量是否大于核心线程数量,如果大于(不可能大于),会先创建核心线程数量(核心数量和最大线程数量相等)。
创建线程是在ThreadPoolExecutor构造方法参数的默认工厂参数创建,
通过execute(任务)传入任务时,无论任务数量是否大于线程池数量,如果小于线程池数量,会分配线程执行任务。若任务大于线程池数量,
超过的任务会被放在阻塞队列排队等待线程空下来。
关闭线程池,shutdown()会将阻塞队列的线程任务执行完再关闭,shutdownNow()会立即关闭,清空阻塞队列的线程任务,关闭线程池。
2.什么是守护线程?它与普通线程有什么区别?
守护线程:
守护线程也称为后台线程(非守护线程又叫前台线程)是通过普通线程调用setDaemon(boolean on)方法设置而来的,因此在创建上与普通线程一样,
区别:守护线程的结束时机与普通线程不同,即:进程的结束(守护线程才结束)。
进程结束:当一个进程中的所有普通线程都结束时,进程就会结束,此时会杀掉所有正在运行的守护线程。
什么时候设置守护线程:通常当我们不关心某个线程的任务什么时候停下来时,他可以一直运行,但是程序主要的工作都结束时它应当跟着结束,这样的任务就适合放在守护线程上执行比如GC就是在守护线程上运行的。
3.synchronized关键字的用法
①synchronized 即可以修饰普通方法,也可以修饰静态方法。使用synchronized修饰的方法称为“同步方法”;
- 即:多个线程不能同时在方法内部执行,只能有先后顺序的一个一个执行。
- 将并发操作同一临界资源的过程改为同步执行就可以有效解决并发安全问题。
在静态方法上使用synchronized后,该方法一定是同步方法,由于静态方法属于类,所以一定具有同步效果
②也可以在方法体中使用同步块,可以更准确的控制需要排队执行的代码片段。语法如下:
synchronized(同步监视器对象){
//需要多线程同步执行的代码片段
}
需要注意的是:
①在普通方法中使用同步块,同步监视器可以为任意类型对象,在普通方法中要保证多个线程同步监视器相同,否则同步无效,可以将同步监视器声明为成员变量或指定为this(当前正在调用的对象),视情况而定。
②在静态方法中使用同步块,同步监视器对象为当前类的类对象(Class实例)
4.什么是单例模式?
单利设计模式(Singleton pattern)是java中最简单的设计模式之一,属于创建型模式,它提供了一种创建对象的最佳方式
单利设计模式要保证一个类仅有一个实例,并为该实例提供一个全局都可以访问的方式(类方法)。
好处是:为了节省内存,为了节省时间
开发步骤:
1.私有化构造方法不让外界随意new对象
2.自己在类的内部创建一个对象
3.提供一个公共的访问方式,并返回自己创建的对象
5.单例设计模式对象创建方式:懒汉式和饿汉式有什么区别?如何实现?
区别:①饿汉式对象加载时间过长,占用内存,但为线程安全。懒汉式为线程不安全,但可以通过线程同步机制将懒汉式设置成线程安全的。节约内存
饿汉式:在创建单例模式的类时就直接创建该类对象,在调用方法获取对象时直接返回对象
懒汉式 :又叫按需加载/延时加载,在创建单例模式的类中只声明对象的引用,在调用方法时获取对象时先判断该对象是否为null,为null则创建对象返回
实现:
第一种:饿汉式:
public class Singleton1{
// 私有化构造方法
private Singleton1(){}
//2.在类内部创建对象
private static Singleton1 single = new Singleton1();
//3.提供公有的类方法返回实例
public static Singleton1 getInstance(){
return single;
}
}
第二种饿汉式:使用静态代码块的方式实现,这种方式与第一种类似(给方法上锁),也是一种饿汉式
public class Singleton2 {
//私有化构造方法
private Singleton2(){}
//2.创建私有实例
private staic Singleton2 single2;
//3.与类一起加载编译器加载进内存,只加载一次。
static {
single2 = new Singleton2();
}
//4.提供公有的类方法,返回实例
public static Singleton2 getInstance(){
return single2;
}
}
第一种懒汉式创建:
public class Singleton3 {
//1.私有化构造方法
private Singleton3(){}
//2.创建实例
private static Singleton3 single3;
public synchronized static Singleton3 getInstance{
if(single3 == null){
single3 = new Singleton3
}
return singleton3;
}
}
第二种使用静态内部类方式创建,也是一种懒汉式创建(最优解):
public class Singleton4{
//1.私有化构造方法
private Singleton4(){}
//2.提供私有的静态内部类创建实例。
private static class SingletonInner{
Singleton4 single4 = new Singleton4();
}
public static Singleton4 getInstance(){
return SingletonInner . single4;
}
}
6.如何实现多线程并发安全?
采用同步机制:
实现同步机制有三种方式:
①同步方法。使用synchronized修饰方法则该方法为同步方法,可解决线程安全问题。
②:使用join()方法,分别在需要实现多线程任务的线程中使用join(),控制线程的并发执行。
③:同步块:使用synchronized在方法中定义同步块,在普通方法中定义同步块,同步监视器为该方法所在类的对象。在静态方法中使用同步块,同步监视器对象为该方法的类对象
4.9 集合
1.怎么将集合转换为数组?怎么将数组转换为集合?
集合转数组:
Collection提供了一个方法:toArray(),可以将当前集合转换为一个数组。
如果该数组可用(数组长度>=集合的size时),会将当前集合元素存入该数组后再将数组返回。
如果该数组不可用,会创建一个与参数数组同类型、并且长度与集合size一致的数组,再将集合元素存入并返回。
数组转换为集合
数组转集合:
数组的工具类:Arrays提供了一个静态方法:asList(),
可以将一个数组转换为一个List集合。由于数组是定长的,因此从数组转换来的集合是不可以调用增删元素的等影响数组长度的操作,否则会抛出异常。如果想要向集合中增删元素,需要自行创建一个集合,然后将原集合导入到该集合中即可,所有的集合都支持一个参数类型为Collection的构造方法,作用是在创建在当前集合的同时包含给定集合的所有元素。
2.什么是迭代器?如何使用?
迭代器是一种设计模式
Iterator提供了统一遍历集合元素的统一接口, Collection接口继承了Iterable接口,
每个集合都通过实现Iterable接口中iterator()方法,该方法会返回一个用于遍历当前集合的迭代器对象(Iterator接口的实现类),使用它可以对集合进行遍历(对集合的元素进行迭代操作).
有一点需要注意的是:在迭代元素的过程中不能通过集合的remove()方法删除元素, 否则会抛出ConcurrentModificationException 异常. 但是可以通过Iterator接口中的remove()方法进行删除.
3.什么是泛型?作用是什么?泛型的用处有哪些?
泛型是JDK5之后推出的新特性。
泛型也称为参数化类型,泛型的类型必须是引用类型,有泛型支持的类在使用时若不指定泛型的具体类型则默认为原型Object
泛型的作用:是在编译器检查,如果泛型类型不一致会编译报错。
应用:允许我们在使用一个类中通过其定义的泛型来指定其属性类型、方法的参数类型 或 返回值类型。
泛型在集合中被广泛使用,用来指定集合中的数据类型。
4.迭代器、新循环、foreach在遍历集合时有何不同?
迭代器遍历集合首先要声明一个迭代器对象,通过集合调用iterator()方法返回一个迭代器对象给到声明的迭代器对象引用。通过该迭代器对象循环调用hasNext()方法判断迭代器中是否还有元素,如果有则调用next()取出集合元素。
新循环遍历集合不在使用下标,而是通过在for的小括号里定义元素类型 变量名:集合或数组,然后在大括号中输出,表面上看到是直接通过把集合元素依次给到变量输出,实际遍历集合时底层还是采用迭代器遍历
注:新循环遍历基本数据类型时底层采用的是普通for循环。Collection 的forEach()方法要求传入一个lambda表达式,然后将集合每个元素都顺序的传给lambda表达式中的参数,并执行一次表达式。
forEach与迭代器区别:
与迭代器相比,由于迭代器是用另一个类Iterator来遍历集合,因此他遍历的过程中不允许用集合的方法删除元素
集合有并发安全的实现类,但是仍然不允许多线程操作时使用迭代器遍历与集合方法增删同时进行),否则会抛出异常。
但是集合新添加的forEach()方法在遍历的过程中没有上述限制,因为forEach()是集合自身的方法,
因此对于并发安全的集合的实现类,删除元素和forEach()有很好的互斥性(两个操作不能一起干),可以保证多线程的并发安全。
5.如何对List集合进行排序?
可以使用集合工具类提供的静态方法(类方法)——sort(List list) / sort(List list , new Comparator<?>):Collections.sort(List list);对集合进行自然排序,要求集合的元素所属类必须实现Comparable接口,重写compareTo()方法,用于定义该类元素之间的大小关系,java中很多常用的类都都实现了它,比如String,包装类。如果进行自然排序后还不满足需求,可以再调用sort(List list,new Comparator<?>)重载方法,第二个参数new一个比较器对象,重写compare()方法。
compare()来定义两个要比较的元素的大小规则。通常我们直接以匿名内部类的形式创建,为集合的排序使用。该方法中的两个参数就是要比较的两个元素,方法返回值为比较大小的关系,定义如下:
* 当返回值>0,表示参数1比参数2大
* 当返回值<0,表示参数1比参数2小
* 当返回值=0,表示参数1与参数2相等
* 即:从小到大为参数1-参数2,从大到小为参数2-参数1
4.10集合2
1.请描述Array和ArrayList有何区别?什么时候更适合用Array?
1.Array既可以存储基本数据类型也可以存储引用类型,而 ArrayList只能存放引用类型。
2. Array是指定大小的,长度初始化后不能修改的数组,修改长度时需要手动扩容。而ArrayList是可变数组,需要修改长度时时底层会自动完成扩容操作,使用起来更灵活。
3.Array提供的功能没有ArrayList多。例如:ArrayList提供了size()方法可以获取ArrayList中的具体元素个数,但Array不能通过方法获得具体元素个数。remove()、add()等。
但是如果列表的大小已经指定,可以使用Array进行存储和遍历,而且对于基本数据类型来说,尽管ArrayList可以自动拆装箱,但是效率会相对慢一些。
以下为自己总结:
①Array是数组,一旦初始化以后,其长度就不可修改,其元素的类型也就确定,
只能操作指定类型的数据。
数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。
获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用
②:存储元素的类型: Array即可以存储基本类型也可以存储引用类型。
存放相同数据类型的元素。ArrayList只能存储引用类型,可以存放任意引用类型的数据
③ArrayList是集合的实现类,类中定义了属性和方法,可以通过无参构造创建对象,创建对象时ArrayList底层维护了一个Object[] elementData 数组,用来存储数据,JDK8之前是当创建一个ArrayList对象时,直接创建一个容量为10的数组,JDK8之后当创建一个ArrayList对象时,创建一个空数组,为了优化内存,相当于延迟分配对象数组空间:
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};,ArrayList相当于可变的数组,当第一次添加元素时,扩容为长度为10的数组,当超过第十个时,底层源码会自动扩容到当前数组的1.5倍,底层源码为:
int newCapacity = oldCapacity + (oldCapacity >> 1);
①:当需要存储固定大小的基本数据类型时,使用Array更适合,因为使用ArrayList底层还要进行自动装箱操作,还要进行数组扩容等操作麻烦
2.ArrayList,LinkedList和Vector的区别
相同点:都实现了List接口,存储数据的特点相同:存储有序的、可重复的数据
不同点:
ArrayList和Vector底层是数组,查询数据快,增删数据慢,增删数据时需要依次挪动元素位置。
LinkedList底层是链表,增删快。
LinkedList和ArrayList 是线程不安全的。 Vector是线程安全的,ArrayLis和LinkedList的执行效率要略高于Vector.
3.简述ArrayList的扩容原理
ArrayList底层维护了一个Object[] elementData 数组,用来存储数据,JDK8之前是当创建一个ArrayList对象时,如果不指定长度,直接创建一个容量为10的数组,JDK8之后当创建一个ArrayList对象时,创建一个空数组,为了优化内存,相当于时延迟分配对象数组空间: private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
(什么时候分配容量:)当第一次插入元素时才扩容为一个长度为 10(默认)的对象空间。即第一次调用add添加数据时分配;
transient Object[] elementData;
private static final int DEFAULT_CAPACITY = 10;
当超过10个元素时,底层会自动扩容,(底层源码)是:
int newCapacity = oldCapacity + (oldCapacity >> 1);
即扩容至原容量的1.5倍。
4.LinkedList如何进行元素的查找
①可以通过get(int index); LinkedList的public E get(int index)实现机制: 比较传入的索引值index与集合长度size/2,
如果是index小,那么从第一个元素往后开始循环,直到找到为止;
如果是index大,那么从最后一元素开始倒序循环,直到找到为止。越靠近中间位置的元素,调用get()方法遍历的次数越多,效率就越低,而且随着集合越来越大,get()方法的执行性能也会下降,因此使用LinkedList的时候,不建议使用这种方式读取数据, 可以使用getFirst(),getLast()直接获取集合中第一个和最后一个元素
5.什么是队列?什么是双端队列?什么是栈?存取元素时有什么特点?
队列Queue(接口):java.util.Queue 接口继承自Collection接口,所以队列本身也是一种集合,可以存放一组数据,存取遵循先进先出原则的数据结构( FIFO )
间接实现类有LinkedList类
常用方法有:
1.boolean offer( e);入队操作 ,元素会被追加到队列末尾
2.E poll();出队操作,获取并删除队列中的队首元素
3.E peek();引用队首元素,获取后该元素仍然在队列中双端队列:java.util.Deque 继承自Queue,特点是队列两端都可以做出入队操作。实现类有LinkedList类
除了队列Queue中的出入队方法,新增了
offerFirst()队首入队方法
offerLast()队尾入队方法
pollFirst()队首出队方法
pollLast()队尾出队方法栈:栈可以保存一组元素,存取元素必须遵循先进后出(FILO 或 LIFO(后进先出))原则。只能从栈顶一端存取元素。通常使用栈结构完成“ 后退 "功能。
实现:创建栈有两种方式:
Deque 双端队列如果从同一侧做出入队操作就实现了栈结构,因此Deque(接口)也为栈,提供它出入栈的典型方法:push,pop
入栈也称为压栈,出栈也叫弹栈。出栈时获取数据后,原数据会从栈中移除
另一种是java.util.Stack类——继承自Vector类
6.遍历Map的方式
Map提供了三种遍历方式:
1.遍历所有的key
Map接口提供了一个返回值类型为Set的抽象方法keySet();
将当前Map中所有的key以一个Set集合的形式返回。
遍历该集合等同于遍历Map中所有的key。
2.遍历所有的键值对
Map提供了entrySet()方法
Set entrySet()
将当前Map中的每一组键值对以若干个Entry实例保存,
并且存入一个Set集合后返回。
java.util.Map.Entry
该接口的每一个实例用于表示Map中的一组键值对
该接口提供了两个常用方法:
K getKey() V getValue()
3.遍历所有的value(相对不常用,因为value是可重复的)
Map提供了返回值为Collection的 values()方法,
Collection values()
将当前Map中的所有value以一个集合形式返回
遍历该集合等同于遍历Map中的所有的Value。
三种方式都可以通过新循环、forEach()方法遍历
4.10集合3
1.HashMap扩容原理
JDK1.8HashMap底层采用数组+链表+红黑树形式存储.
当创建HashMap对象时如果没有指定初始化容量,底层会创建一个空的 Node[] table数组。第一次往HashMap中put(添加)元素的时候 ,会使用默认容量16作为数组的初始化长度:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
当HashMap中的元素数量超过 容量*加载因子 时,会进行扩容操作,容量变为原来的2倍,HashMap的加载因子,默认是0.75:
static final float DEFAULT_LOAD_FACTOR = 0.75f;
2.HashMap底层结构
JDK1.8 HashMap底层是一个Node数组,: transient Node<k,V>[] table; (Node<K,V> 类实现了 Map.Entry接口)
当存放元素时,首先会根据Key的hash值找到数据应该保存在table数组的下标位置。
如果数组的位置上没有数据 ,直接将这这个键值对保存在该位置上。
如果数组的位置上已经有数据了,即发生了哈希冲突(哈希碰撞),也就是两个对象的key和hash值相等,那么则需要通过Key的equals()方法判断这两个对象是否为同一个对象。如果是,那么原本存储Key的value值会被新值所替换。如果不是同一个对象,则采用链式地址法,把新的键值对对象保存到旧的键值对对象(Node<K,V>)的next变量中,形成单向链表结构,即是链表的尾部插入。
当链表过长时,查询效率会下降,所以JDK8之后新增了红黑树作为底层数据结构,如果链表长长度超过8并且数组长度大于64时,
static final int TREEIFY_THRESHOLD = 8;
static final int MIN_TREEIFY_CAPACITY = 64;
hashMap会把这个链表转成红黑树来存储(如果数组长度没有超过64会强制扩容而不是转成红黑树,因为红黑树增删数据效率低,数组扩容后减少hash冲突几率)
当链表长度小于6时会从红黑树转回链表
static final int UNTREEIFY_THRESHOLD = 6;
jdk1.7HashMap底层采用数组+链表形式;
创建HashMap时就指定数组容量为16,加载因子为0.75,
当存放元素时,首先会根据Key的hashCode值找到数据应该保存在table数组的下标位置。
如果数组的位置上没有数据 ,直接将这这个键值对保存在该位置上。
如果数组的位置上已经有数据了,即发生了哈希冲突(哈希碰撞),也就是两个对象的key和hash值相等,那么则需要通过Key的equals()方法判断这两个对象是否为同一个对象。如果是,那么原本存储Key的value值会被新值所替换。如果不是同一个对象,也是采用链式存储,直接在数组的该位置插入Entry[]形成单向链表结构,即是从链表的头部插入。当元素个数大于数组容量*加载因子时,数组扩容为原来2倍。
3.HashSet存储原理
HashSet底层由HshMap实现,利用了HashMap的key不能重复的特性直接将要存储的元素作为key存储到HashMap中。HashSet即是HashMap的Key。
第一次往HashSet中add()元素时,底层实际调用map的put添加元素,map的键(Key)是HashSet add()的元素,value为每次创建一个Object对象。
4.List和Set的区别
1.Set 接口实例存储不重复的数据,不强调Set的无序性,因为其间接实现类LinkedHashSet提供了双向链表通过前驱变量存放前一个元素的地址和后继变量存放后一个元素的地址实现有序遍历集合的操作。List 接口实例存储的是有序的,可以重复的元素
2.Set检索效率低,删除和插入效率高,
3 . List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。 查找元素效率高,插入删除效率低,因为会引起其他元素位置改变,解决办法是倒序删除。<实现类有ArrayList,LinkedList,Vector> 。
Set:检索元素效率低 ,删除和插入效率高,<直接实现类有HashSet,间接实现类有LinkedHashSet,TreeSet>一、存储元素的方式: java.util.List:线性表,可以保存重复元素并且有序,可通过下标操作。 java.util.Set: 不可重复集合,这里的重复元素判定是依靠元素自身的equals方法比较的结果而定,为true就认为是重复元素则不添加。 二、添加对象: List添加对象没有要求,Set添加对象,要求对象所在的类必须重写equals()和hasCode(),相等的对象必须具有相等的散列码。 三、检索效率 Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 List:类似动态数组,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。 List接口有三个直接实现类,Set有一个直接实现类,两个间接实现类
5.HashMap和Hashtable的区别
相同点:HashMap 和 Hashtable都实现了Map接口,Cloneable(可克隆),Serializable(可序列化)这三个接口不同点:1,HashMap线程不安全的,效率高;允许key和value存储null2,Hashtable:线程安全的,效率低;key和value不能存储null3.Hashtable提供了对键的列举(Enumeration).
6.为什么HashMap查询速度快?
答:因为HashMap底层采用数组+链表+红黑树形式存储。
查找数据时,首先会根据查询元素的key的hash值找到数据应该保存在node[]数组的下标位置,通过数组下标快速定位到该位置上。
如果这个位置上什么都没有,则直接返回null。
如果这个位置上只有一个键值对对象,并且key的equals() 比较为true,则直接将这个键值对对象(Node<K,V>)的value返回。
如果这个位置上有单向链表,则会将该key与单向链表上每一个节点的key进行equals()比较,如果所有的节点的equals比较结果都为false。
则返回null.如果其中一个节点的key进行equals比较为true,则返回这个节点的value.
红黑树遍历:如果当前的数组,后面是红黑树,采用前序遍历,则从根节点开始遍历,然后遍历左子节点,右子节点。仍然是与节点键值对的key的equalse()比较,比较结果为true的情况则返回该节点上键值对的value值,比较结果为false则返回null.
五、泛型
5.1.什么是泛型?作用是什么?都可以用在什么地方?
泛型又叫参数化类型,泛型只能是引用类型。
作用:在编译器检查,如果泛型不一致会编译报错。
应用:允许一个类通过指定的泛型来指定其属性 类型,方法参数类型,方法返回值类型,在集合中使用泛型来指定其数据类型。
XML(可扩展标记语言)
xml文件的语法要求有哪些?
1、XML 文档必须有根元素,有且只能有一个根元素,在根元素里面可以写很多子节点
2、XML 声明:XML 1.0版本声明文件的可选部分,XML1.1版本必须有声明部分。如果存在需要放在文档的第一行
3,、除了自闭合标签,所有的 XML 元素都必须有一个关闭标签
自闭合标签形式:,定义属性只能在<>里的/之前定义。
4,XML的所有标签名是自定义的,所以XML 标签对大小写敏感
5、标签成对存在,自闭合标签,可以独立存在
6.XML 中的注释采用:
7.常用的xml解析方式
XML解析常用方式:
1.DOM(Document Object Model,文档对象模型)
是官方提供的平台无关的解析方式。
会将一个XML文档内容以树的结构一次性加载到内存中,可以对XML文档的内容进行随机访问或修改的操作。但是对大文档来说解析效率低,内存成本高。
2.SAX(以事件为驱动)
以事件为驱动,需要哪些数据再加载和解析哪些内容,解析速度快,占用内存少,但是不会将内容加载到内存,使用起来不方便,反而会增加程序负担。3.JDOM(JAVADOM用的是集合)
是java和DOM的结合体,是基于java平台使用的,简单高效,但是需要加载整个文档,对内存容量要求高。4.DOM4J
是一个java的XML API,类似于JDOM,用来读写xml文件,性能优异,功能强大,简单易用,且开放源代码,是目前最流行、最好用的xml解析工具,解析xml速度最快
8.什么是maven
1.Maven 是 Apache 开源组织奉献的一个开源项目
2、Maven 的本质是一个项目管理工具,将项目开发和管理过程抽象成一个项目对象模型(POM)。开发人员只需做一些简单的配置,就可以批量完成项目的构建、报告和文档的生成工作。
3、Maven 是跨平台的,这意味着无论是在 Windows 上,还是在 Linux 或者 Mac 上,都可以使用同样的命令。
综上所诉,Maven 作为一个构建工具,不仅能帮我们自动化构建,还能够抽象构建过程,提供构建任务实现;它跨平台,对外提供了一致的操作接口,这一切足以使它成为优秀的、流行的构建工具
**Maven 可以统一管理所有的依赖 jar,**甚至是不同的版本。程序员也可以动态地将自己写好的模块打包成 jar 包让它管理。需要的时候,可以直接通过简单的描述文件告诉 Maven,它会自动帮助程序员找出来,集成到项目中。并且它提供了中央仓库,能帮我们自动下载构件。总结Maven作用:
Maven 统一集中管理好所有的依赖包,不需要程序员再去寻找。
对应第三方组件用到的共同 jar包,Maven 自动解决重复和冲突问题。
Maven 作为一个开放的架构,提供了公共接口,方便同第三方插件集成。程序员可以将自己需要的插件,动态地集成到 Maven,从而扩展新的管理功能。
Maven 可以统一每个项目的构建过程,实现不同项目的兼容性管理。
扩展: jar包是将一系列的类、接口等相关文件压缩打包后形成的文件,表现为扩展名为.jar的文件。
反射
1.什么是反射
>Java反射机制是一种动态机制,它允许程序运行起来后再确定实例化对象,调用方法和操作属性,可以提高代码的灵活度。
但是反射会带来较慢的运行速度和更多的系统开销,所以不能过度依赖反射机制。使用反射的作用:
1.获取类对象
2.使用反射实例化对象
3.使用反射调用方法
4.使用反射操作属性
在JDK中,主要由以下类来实现Java反射机制,这些类(除了第一个)都位于java.lang.reflect包中:
Class类:代表一个类,位于java.lang包下。newInstance()
Field类:代表类的成员变量(成员变量也称为类的属性)。 Object get(Object obj),
需要指定对象,想要获取那个对象的属性值就哪个对象 getDeclaredField(); 获取属性,可以获取私有和非私有属性
Field[] getFields();获取非私有属性
getDeclaredFields();获取所有属性包括私有Method类:代表类的方法。
getMethod(),
Methods[] getMehtods();
getDeclaredMethod();
getDeclaredMethods();Constructor类:代表类的构造方法。
Constructor getConstructor(Class<?>… parameterTypes);参数列表是可变参数;当参数列表为空,获取到的是无参构造Array类:提供了动态创建数组,以及访问数组的元素的静态方法。
2.反射里获取类对象方式有三种:
1.类名.class —基本类型只有这种方式获取类对象的方式
2. Class.forName(String className);参数为类的完全限定名,即:包名.类名
3.使用类加载器ClassLoader
3.使用反射的作用:
1.获取类对象:
三种方式:1.类名.class —基本类型只有这种方式获取类对象的方式
- Class.forName(String className);参数为类的完全限定名,即:包名.类名
3.使用类加载器ClassLoader
2.使用反射实例化对象
3.使用反射调用方法
4.使用反射操作属性
注解
1…注解的分类,你用过哪些注解,它们的作用是什么?
注解(Annocation)的概念:又叫标注,是从Java5开始增加的一种引用数据类型,是代码里的特殊标志,注解本质上就是代码中的特殊标记,通过这些标记可以在编译,类加载,以及运行时执行指定的处理。通过@interface 注解名称{ 注解成员;}定义。
注解的分类:
1.五个JDK注解:
1.@Override 限定父类重写方法
2.@Deprecated 标示已过时
3.@SuppressWarnings 抑制编译器警告
4.@SafeVarargs “堆污染”警告
5.@FunctionalInterface 函数式接口
2.元注解
用于描述注解的注解 ,通常配合元注解完成自定义注解
元注解主要有:
@Retention, 定义注解的生命周期,只能指定一个,
@Documented,
@Target, 用来指定注解可以出现的位置,比如出现在类上、方法上、属性上、参数上。 通过ElementType可以指定多个位置,在大括号中用逗号隔开
@Inherited, 指定继承关系
@Repeatable
3.自定义注解
使用@interface 注解名{
注解体;
例如:
类型 属性名() default 默认值;
11.谈谈你对socket的理解
Socket也叫做套接字,Java Socket是实现Java网络编程的机制,其中服务器端的套接字成为ServerSocket,而客户端的套接字称为Socket。每一个Socket都由IP地址和端口号唯一确定。
Socket可以分为两种类型:面向连接的Socket通信协议(TCP)和无连接的Socket通信协议(UDP)。
基于TCP的Socket通信过程分为三个步骤:服务器监听、客户端请求、连接确认。
Socket的生命周期可以分为三个阶段:打开Socket,使用socket收发数据和关闭socket。在Java语言中,使用socket实现客户端和服务器端的进行数据交换,与数据流的操作关系密切。Socket提供了getInputStream()和getOutputStream()来写出和读入字节数据通过网络发送给对方。
单例设计模式
1.什么是单例设计模式?
单利设计模式(Singleton pattern)是java中最简单的设计模式之一,属于创建型模式,
它提供了一种创建一个对象的最佳方式。
单利设计模式要保证一个类仅有一个实例,并提供一个全局都可以访问的方式
好处是:为了节省内存,为了节省时间
2. 单例设计模式的开发步骤
1.私有化构造方法不让外界随意new对象
2.自己在类的内部创建一个对象
3.提供一个公共的访问方式,并返回自己创建的对象
3.常用的实现方式:
- 1.懒汉式()
- 2.饿汉式()