目 录
**第一章 初识Java **
1. Java跨平台原理(字节码文件、虚拟机)
2. Java的安全性
3. Java三大版本
4. Java开发运行过程
5. Java开发环境配置
6. 什么是JVM?什么是JDK? 什么是JRE?
7. Java三种注释类型
**第二章 数据类型和运算符 **
1. 8种基本数据类型及其字节数
2. i++和++i的异同之处
3. &和&&的区别和联系,|和||的区别和联系
4. 用最有效率的方法算出2乘以8等于多少
5. 基本数据类型的类型转换规则
**第三章 流程控制 **
1. 三种流程控制结构
2. if多分支语句和switch多分支语句的异同之处
3. while和do-while循环的区别
4. break和continue的作用
5. 请使用递归算法计算n!
6. 递归的定义和优缺点
**第四章 数组 **
1. 数组的特征
2. 请写出冒泡排序代码
3. 请写出选择排序的代码
4. 请写出插入排序的代码
5. 可变参数的作用和特点
**第五章 面向对象 **
1. 类和对象的关系
2. 面向过程和面向对象的区别
3. 方法重载和方法重写(覆盖)的区别
4. this和super关键字的作用
5. static关键字的作用(修饰变量、方法、代码块)
6. final和abstract关键字的作用
7. final、finally、finalize的区别
8. 写出java.lang.Object类的六个常用方法
9. private/默认/protected/public权限修饰符的区别
10. 继承条件下构造方法的执行过程
11. ==和equals的区别和联系
12. 多态的技能点(前提条件,向上转型、向下转型)
13. 接口和抽象类的异同之处
14. 简述Java的垃圾回收机制
**第六章 异常处理 **
1. Error和Exception的区别
2. Checked异常和Runtime异常的区别
3. Java异常处理try-catch-finally的执行过程
4. 异常处理中throws和throw的区别
**第七章 常用工具类 **
1. 基本数据类型和包装类
2. Integer与int的区别
3. String类为什么是final的
4. String、StringBuffer、StringBuilder区别与联系
5. String类型是基本数据类型吗?基本数据类型有哪些
6. String s=“Hello”;s=s+“world!”;执行后,s内容是否改变?
7. String s = new String(“xyz”);创建几个String Object?
8. 下面这条语句一共创建了多少个对象:String s=“a”+“b”+“c”+“d”
9. java.sql.Date和java.util.Date的联系和区别
10. 使用递归算法输出某个目录下所有文件和子目录列表
**第八章 集合 **
1. Java集合体系结构(List、Set、Collection、Map的区别和联系)
2. Vector和ArrayList的区别和联系
3. ArrayList和LinkedList的区别和联系
4. HashMap和Hashtable的区别和联系
5. HashSet的使用和原理(hashCode()和equals())
6. TreeSet的原理和使用(Comparable和comparator)
7. 集合和数组的比较(为什么引入集合)
8. Collection和Collections的区别
**第九章 IO流 **
1. 输入流和输出流联系和区别,节点流和处理流联系和区别
2. 字符流字节流联系区别;什么时候使用字节流和字符流?
3. 列举常用字节输入流和输出流并说明其特点,至少5对。
4. 说明缓冲流的优点和原理
5. 序列化的定义、实现和注意事项
6. 使用IO流完成文件夹复制(结合递归)
**第十章 多线程 **
1. 进程和线程有什么联系和区别?
2. 创建线程的两种方式分别是什么,优缺点是什么?
3. Java创建线程后,调用start()方法和run()的区别
4. 线程的生命周期
5. 如何实现线程同步?
6. 关于同步锁的更多细节
7. 简述sleep( )和wait( )有什么区别?
8. Java中实现线程通信的三个方法的作用是什么?
**第十一章 网络编程 **
1. IP地址和端口号
2. 介绍OSI七层模型和TCP/IP模型
3. TCP协议和UDP协议的比较
4. 什么是Socket编程
5. 简述基于TCP和UDP的Socket编程的主要步骤
**第十二章 反射技术 **
1. Java反射技术主要实现类有哪些,作用分别是什么?
2. Class类的作用?生成Class对象的方法有哪些?
3. 反射的使用场合和作用、及其优缺点
**第十三章 设计模式入门 **
1. 什么是设计模式,设计模式的作用。
2. 面向对象设计原则有哪些
3. 23种经典设计模式都有哪些,如何分类。
4. 写出简单工厂模式的示例代码
5. 写出单例模式的示例代码
6. 请对你所熟悉的一个设计模式进行介绍
第一章 初识Java
Java跨平台原理(字节码文件、虚拟机)
Java的安全性
语言层次的安全性主要体现在:
底层的安全性可以从以下方面来说明
Java在字节码的传输过程中使用了公开密钥加密机制(PKC)。
在运行环境提供了四级安全性保障机制:
字节码校验器 -类装载器 -运行时内存布局 -文件访问限制
Java三大版本
Java2平台包括标准版(J2SE)、企业版(J2EE)和微缩版(J2ME)三个版本:
Standard Edition(标准版) J2SE 包含那些构成Java语言核心的类。
比如:数据库连接、接口定义、输入/输出、网络编程
Enterprise Edition(企业版) J2EE 包含J2SE 中的类,并且还包含用于开发企业级应用的类。
比如:EJB、servlet、JSP、XML、事务控制
Micro Edition(微缩版) J2ME 包含J2SE中一部分类,用于消费类电子产品的软件开发。
比如:呼机、智能卡、手机、PDA、机顶盒
他们的范围是:J2SE包含于J2EE中,J2ME包含了J2SE的核心类,但新添加了一些专有类
应用场合,API的覆盖范围各不相同。
Java开发运行过程
在安装好JDK并配置好path、classpath后开发运行步骤如下:
1、可以用任何文本编辑器创建并编辑Java源程序,Java源程序用“.java”作为文件扩展名
2、编译Java源程序编译器,使用命令“javac”编译“java源程序文件名**.java”。最后编译成Java虚拟机能够明白的指令集合,且以字节码的形式保存在文件中。通常,字节码文件以“.class”作为扩展名。**
3、执行java程序,使用“java”命令运行class(字节码)文件“java文件名”,Java解释器会读取字节码,取出指令并且翻译成计算机能执行的机器码,完成运行过程。
Java开发环境配置
具体配置步骤如下:
0)找到自己的jdk安装路径,如:C:\Java\jdk1.7.0_60\bin
1)右击桌面“我的电脑”,选择“属性”
2)选中“高级系统设置”–>高级–>环境变量设置
3)在系统变量中找到“path”并选中,点击“编辑”,
4)变量值栏按键盘“home”键,输入英文的“;”
5)将第0)步准备的路径复制过来就行。点一些列“确定”完成配置
什么是JVM?什么是JDK? 什么是JRE?
bin:最主要的是编译器(javac.exe)
include:java和JVM交互用的头文件
lib:类库
jre:java运行环境
(注意:这里的bin、lib文件夹和jre里的bin、lib是不同的)总的来说JDK是用于java程序的开发,而jre则是只能运行class而没有编译的功能。eclipse、idea等其他IDE有自己的编译器而不是用JDK bin目录中自带的,所以在安装时你会发现他们只要求你选jre路径就ok了。
4、JDK,JRE,JVM三者关系概括如下:
jdk是JAVA程序开发时用的开发工具包,其内部也有JRE运行环境JRE。JRE是JAVA程序运行时需要的运行环境,就是说如果你光是运行JAVA程序而不是去搞开发的话,只安装JRE就能运行已经存在的JAVA程序了。JDk、JRE内部都包含JAVA虚拟机JVM,JAVA虚拟机内部包含许多应用程序的类的解释器和类加载器等等。
Java三种注释类型
共有单行注释、多行注释、文档注释3种注释类型。使用如下:
/*System.out.println(“a”);
System.out.println(“b”);
System.out.println(“c”);*/
/**
* 子类 Dog
* @author Administrator
*/
public class Dog extends Animal{}
数据类型和运算符
8种基本数据类型及其字节数
数据类型 | 关键字 | 字节数 | |
---|---|---|---|
数值型 | 整数型 | byte | 1 |
short | 2 | ||
int | 4 | ||
long | 8 | ||
浮点型 | float | 4 | |
double | 8 | ||
布尔型 | boolean | 1**(位)** | |
字符型 | char | 2 |
i++和++i的异同之处
共同点:
1、i++和++i都是变量自增1,都等价于i=i+1
2、如果i++,++i是一条单独的语句,两者没有任何区别
3、i++和++i的使用仅仅针对变量。 5++和++5会报错,因为5不是变量。
不同点:
如果i++,++i不是一条单独的语句,他们就有区别
i++ :先运算后增1。如:
int x=5; int y=x++; System.out.println(“x=”+x+", y="+y); //以上代码运行后输出结果为:x=6, y=5
++i : 先增1后运算。如:
int x=5; int y=++x; System.out.println(“x=”+x+", y="+y); //以上代码运行后输出结果为:x=6, y=6
&和&&的区别和联系,|和||的区别和联系
&和&&的联系(共同点):
&和&&都可以用作逻辑与运算符,但是要看使用时的具体条件来决定。
操作数1&操作数2,操作数1&&操作数2, 表达式1&表达式2,表达式1&&表达式2,
情况1:当上述的操作数是boolean类型变量时,&和&&都可以用作逻辑与运算符。
情况2:当上述的表达式结果是boolean类型变量时,&和&&都可以用作逻辑与运算符。
表示逻辑与(and),当运算符两边的表达式的结果或操作数都为true时,整个运算结果才为true,否则,只要有一方为false,结果都为false。
&和&&的区别(不同点):
(1)、&逻辑运算符称为逻辑与运算符,&&逻辑运算符称为短路与运算符,也可叫逻辑与运算符。
对于&:无论任何情况,&两边的操作数或表达式都会参与计算。
对于&&:当&&左边的操作数为false或左边表达式结果为false时,&&右边的操作数或表达式将不参与计算,此时最终结果都为false。
综上所述,如果逻辑与运算的第一个操作数是false或第一个表达式的结果为false时,对于第二个操作数或表达式是否进行运算,对最终的结果没有影响,结果肯定是false。推介平时多使用&&,因为它效率更高些。
|和||的区别和联系与&和&&的区别和联系类似
用最有效率的方法算出2乘以8等于多少
使用位运算来实现效率最高。位运算符是对操作数以二进制比特位为单位进行操作和运算,操作数和结果都是整型数。对于位运算符“<<”, 是将一个数左移n位,就相当于乘以了2的n次方,那么,一个数乘以8只要将其左移3位即可,位运算cpu直接支持的,效率最高。所以,2乘以8等于几的最效率的方法是2 << 3。
基本数据类型的类型转换规则
基本类型转换分为自动转换和强制转换。
自动转换规则:容量小的数据类型可以自动转换成容量大的数据类型,也可以说低级自动向高级转换。这儿的容量指的不是字节数,而是指类型表述的范围。
强制转换规则:高级变为低级需要强制转换。
如何转换:
(1)、赋值运算符“=”右边的转换,先自动转换成表达式中级别最高的数据类型,再进行运算。
(2)、赋值运算符“=”两侧的转换,若左边级别>右边级别,会自动转换;若左边级别 == 右边级别,不用转换;若左边级别 < 右边级别,需强制转换。
(3)、可以将整型常量直接赋值给byte, short, char等类型变量,而不需要进行强制类型转换,前提是不超出其表述范围,否则必须进行强制转换。
第三章 流程控制
三种流程控制结构
流程控制方式采用结构化程序设计中规定的三种基本流程结构,即:顺序结构、分支结构和循环结构
if多分支语句和switch多分支语句的异同之处
相同之处:都是分支语句,多超过一种的情况进行判断处理。
不同之处:
while和do-while循环的区别
while先判断后执行,第一次判断为false,循环体一次都不执行
do while先执行 后判断,最少执行1次。
如果while循环第一次判断为true, 则两种循环没有区别。
break和continue的作用
break: 结束当前循环并退出当前循环体。
break还可以退出switch语句
continue: 循环体中后续的语句不执行,但是循环没有结束,继续进行循环条件的判断(for循环还会i++)。continue只是结束本次循环。
请使用递归算法计算n!
public class MyDemo3 {
public static void main(String[] args) {
int r = jieCheng(5);
System.out.println("结果:"+r);
}
private static int jieCheng(int i) {
if(i==1){
return 1;
}else{
return i*jieCheng(i-1);
}
}
}
递归的定义和优缺点
递归算法是一种直接或者间接地调用自身算法的过程。在计算机编写程序中,递归算法对解决一大类问题是十分有效的,它往往使算法的描述简洁而且易于理解。
递归算法解决问题的特点:
(1) 递归就是在过程或函数里调用自身。
(2) 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。
(3) 递归算法解题通常显得很简洁,但运行效率较低。所以一般不提倡用递归算法设计程序。
(4) 在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。所以一般不提倡用递归算法设计程序。
第四章 数组
数组的特征
请写出冒泡排序代码
冒泡排序算法
1.冒泡排序原理:
数组中的元素两两比较,大的往后放,经过一轮比较后,最大的元素,就出现在了最后面。
2.冒泡排序源代码:
public class MyTest1 {
public static void main(String[] args) {
//冒泡排序:数组中的元素两两比较,大的往后放,经过一轮比较后,最大的元素,就出现在了最后面。
int[] arr = {
24, 69, 80, 57, 13, 20, 30, 1, 0, -1};
// tuiDao(arr);
//外层循环控制轮次
/*for (int j = 0; j arr[i + 1]) {
//值交换
int t = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = t;
}
}
}
System.out.println(Arrays.toString(arr));*/
//外层循环控制轮次
for (int i = 0; i < arr.length - 1; i++) {
//里层循环进行两两比较
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int t = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = t;
}
}
}
System.out.println(Arrays.toString(arr));
}
private static void tuiDao(int[] arr){
//第一轮:比较了四次
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i] > arr[i + 1]) {
//值交换
int t = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = t;
}
}
System.out.println(Arrays.toString(arr));
//第二轮:比较三次
for (int i = 0; i < arr.length - 1 - 1; i++) {
if (arr[i] > arr[i + 1]) {
//值交换
int t = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = t;
}
}
System.out.println(Arrays.toString(arr));
//第三轮:比较了2次
for (int i = 0; i < arr.length - 1 - 1 - 1; i++) {
if (arr[i] > arr[i + 1]) {
//值交换
int t = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = t;
}
}
System.out.println(Arrays.toString(arr));
//第四轮:比较了1次
for (int i = 0; i < arr.length - 1 - 1 - 1 - 1; i++) {
if (arr[i] > arr[i + 1]) {
//值交换
int t = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = t;
}
}
System.out.println(Arrays.toString(arr));
}
}
请写出选择排序的代码
1.选择排序原理:从0索引处的元素开始,依次跟他后面的元素挨个比较,小的往前放,经过一轮比较后,最小的元素就出现在最前面
案例:
public class MyTest2 {
public static void main(String[] args) {
//选择排序:从0索引处的元素开始,依次跟他后面的元素挨个比较,小的往前放,经过一轮比较后,最小的元素就出现在最前面
int[] arr = {
24, 69, 80, 57, 13};
tuiDao(arr);
System.out.println("===============");
for(int index=0;index<arr.length-1;index++){
for(int i=0+index;i<arr.length;i++){
if(arr[index]>arr[i]){
int temp=arr[index];
arr[index]=arr[i];
arr[i]=temp;
}
}
}
System.out.println(Arrays.toString(arr));
}
private static void tuiDao(int[] arr) {
//第一轮:从角标为0开始
int index=0;
for(int i=0+index;i<arr.length;i++){
if(arr[index]>arr[i]){
int temp=arr[index];
arr[index]=arr[i];
arr[i]=temp;
}
}
System.out.println(Arrays.toString(arr));
//第二轮:从角标为1开始
index=1;
for(int i=0+index;i<arr.length;i++){
if(arr[index]>arr[i]){
int temp=arr[index];
arr[index]=arr[i];
arr[i]=temp;
}
}
System.out.println(Arrays.toString(arr));
//第三轮:从角标为2开始
index=2;
for(int i=0+index;i<arr.length;i++){
if(arr[index]>arr[i]){
int temp=arr[index];
arr[index]=arr[i];
arr[i]=temp;
}
}
System.out.println(Arrays.toString(arr));
//第四轮:从角标为3开始
index=3;
for(int i=0+index;i<arr.length;i++){
if(arr[index]>arr[i]){
int temp=arr[index];
arr[index]=arr[i];
arr[i]=temp;
}
}
System.out.println(Arrays.toString(arr));
}
}
请写出插入排序的代码
1.直接插入排序原理:每次拿后面的一个元素,插入到之前的一个有序序列当中,使之仍保持有序.
案例:
public class MyTest3 {
public static void main(String[] args) {
//直接插入排序
//每次拿后面的一个元素,插入到之前的一个有序序列当中,使之任保持有序.
int[] arr = {
10, 3, 20, 0, 1,-1,200,50,30,20,15};
for(int i=0;i<arr.length;i++){
int j=i;
while(j>0 && arr[j]<arr[j-1]){
int temp=arr[j];
arr[j]=arr[j-1];
arr[j-1]=temp;
j--;
}
}
System.out.println(Arrays.toString(arr));
}
}
可变参数的作用和特点
总结1:可变参数
1.可变参数的形式 …
2.可变参数只能是方法的形参
3.可变参数对应的实参可以0,1,2…个,也可以是一个数组
4.在可变参数的方法中,将可变参数当做数组来处理
5.可变参数最多有一个,只能是最后一个
6.可变参数好处:方便 简单 减少重载方法的数量
7.如果定义了可变参数的方法,不允许同时定义相同类型数组参数的方法
总结2:数组做形参和可变参数做形参联系和区别
联系:
1.实参都可以是数组;2.方法体中,可变参数当做数组来处理
区别:
1.个数不同 可变参数只能有一个 数组参数可以多个
2.位置不同 可变参数只能是最后一个 数组参数位置任意
3.实参不同 可变参数实参可以0,1,2…个,也可以是一个数组,数组的实参只能是数组
第五章 面向对象
类和对象的关系
类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模板。
类和对象好比图纸和实物的关系,模具和铸件的关系。
比如人类就是一个概念,人类具有身高,体重等属性。人类可以做吃饭、说话等方法。
小明就是一个具体的人,也就是实例,他的属性是具体的身高200cm,体重180kg,他做的方法是具体的吃了一碗白米饭,说了“12345”这样一句话。
面向过程和面向对象的区别
两者都是软件开发思想,先有面向过程,后有面向对象。在大型项目中,针对面向过程的不足推出了面向对象开发思想。
比喻
蒋介石和Mao泽东分别是面向过程和面向对象的杰出代表,这样充分说明,在解决复制问题时,面向对象有更大的优越性。
面向过程是蛋炒饭,面向对象是盖浇饭。盖浇饭的好处就是“菜”“饭”分离,从而提高了制作盖浇饭的灵活性。饭不满意就换饭,菜不满意换菜。用软件工程的专业术语就是“可维护性”比较好,“饭” 和“菜”的耦合度比较低。
区别
方法重载和方法重写(覆盖)的区别
英文 | 位置不同 | 作用不同 | |
---|---|---|---|
重载 | overload | 同一个类中 | 在一个类里面为一种行为提供多种实现方式并提高可读性 |
重写 | override | 子类和父类间 | 父类方法无法满足子类的要求,子类通过方法重写满足要求 |
修饰符 | 返回值 | 方法名 | 参数 | 抛出异常 | |
---|---|---|---|---|---|
重载 | 无关 | 无关 | 相同 | 不同 | 无关 |
重写 | 大于等于 | 小于等于 | 相同 | 相同 | 小于等于 |
this和super关键字的作用
this是对象内部指代自身的引用
super代表对当前对象的直接父类对象的引用
static关键字的作用(修饰变量、方法、代码块)
static可以修饰变量、方法、代码块和内部类
.static变量和非static变量的区别(都是成员变量,不是局部变量)
1.在内存中份数不同
不管有多少个对象,static变量只有1份。对于每个对象,实例变量都会有单独的一份
static变量是属于整个类的,也称为类变量。而非静态变量是属于对象的,也称为实例变量
2.在内存中存放的位置不同
静态变量存在方法区中, 实例变量存在堆内存中 *
3.访问的方式不同
实例变量: 对象名.变量名 stu1.name=“小明明”;
静态变量:对象名.变量名 stu1.schoolName=“西二旗小学”; 不推荐如此使用
类名.变量名 Student.schoolName=“东三旗小学”; 推荐使用
4.在内存中分配空间的时间不同
实例变量:创建对象的时候才分配了空间。静态变量:第一次使用类的时候
Student.schoolName=“东三旗小学”;或者Student stu1 = new Student(“小明”,“男”,20,98);
final和abstract关键字的作用
final和abstract是功能相反的两个关键字,可以对比记忆
class Test {
public static void main(String[] args) {
final Dog dog = new Dog(“欧欧”);
dog.name = “美美”;//正确
dog = new Dog(“亚亚”);//错误
}
}
final、finally、finalize的区别
写出java.lang.Object类的六个常用方法
public boolean equals(java.lang.Object) 比较内容
public native int hashCode() 哈希码
public java.lang.String toString() 变成字符串
public final native java.lang.Class getClass() 获取类结构信息
protected void finalize() throws java.lang.Throwable 垃圾回收前执行的方法
protected native Object clone() throws java.lang.CloneNotSupportedException 克隆
public final void wait() throws java.lang.InterruptedException 多线程中等待功能
public final native void notify() 多线程中唤醒功能
public final native void notifyAll() 多线程中唤醒所有等待线程的功能
private/默认/protected/public权限修饰符的区别
访问控制 | public | protected | 默认 | private |
---|---|---|---|---|
同一类中成员 | 是 | 是 | 是 | 是 |
同一包中其它类 | 是 | 是 | 是 | |
不同包中的子类 | 是 | 是 | ||
不同包中对非子类 | 是 |
类的访问权限只有两种
public 公共的 可被同一项目中所有的类访问。 (必须与文件名同名)
default 默认的 可被同一个包中的类访问。
成员(成员变量或成员方法)访问权限共有四种:
public 公共的 可以被项目中所有的类访问。(项目可见性)
protected 受保护的 可以被这个类本身访问;同一个包中的所有其他的类访问;被它的子类(同一个包以及不同包中的子类)访问。(子类可见性)
default 默认的 被这个类本身访问;被同一个包中的类访问。(包可见性)
private 私有的 只能被这个类本身访问。(类可见性)
继承条件下构造方法的执行过程
继承条件下构造方法的调用规则如下:
==和equals的区别和联系
==:
a) 基本类型,比较的是值
b) 引用类型,比较的是地址
c) 不能比较没有父子关系的两个对象
equals()
a) 系统类一般已经覆盖了equals(),比较的是内容。
b) 用户自定义类如果没有覆盖equals(),将调用父类的equals(比如是Object),而Object的equals的比较是地址(return (this == obj);)
c) 用户自定义类需要覆盖父类的equals()
注意:Object的==和equals比较的都是地址,作用相同
多态的技能点(前提条件,向上转型、向下转型)
实现多态的三个条件
向上转型 Person person = new Student()
向下转型 Student stu = (Student)person;
接口和抽象类的异同之处
相同点
两者的区别主要体现在两方面:语法方面和设计理念方面
语法方面的区别是比较低层次的,非本质的,主要表现在:
二者的主要区别还是在设计理念上,其决定了某些情况下到底使用抽象类还是接口。
简述Java的垃圾回收机制
传统的C/C++语言,需要程序员负责回收已经分配内存。显式回收垃圾回收的缺点:
Java语言不需要程序员直接控制内存回收,是由JRE在后台自动回收不再使用的内存,称为垃圾回收机制。
垃圾回收机制的特点
第六章 异常处理
Error和Exception的区别
Checked异常和Runtime异常的区别
Java异常处理try-catch-finally的执行过程
try-catch-finally程序块的执行流程以及执行结果比较复杂。基本执行过程如下:
程序首先执行可能发生异常的try语句块。如果try语句没有出现异常则执行完后跳至finally语句块执行;如果try语句出现异常,则中断执行并根据发生的异常类型跳至相应的catch语句块执行处理。catch语句块可以有多个,分别捕获不同类型的异常。catch语句块执行完后程序会继续执行finally语句块。finally语句是可选的,如果有的话,则不管是否发生异常,finally语句都会被执行。
需要注意的是即使try和catch块中存在return语句,finally语句也会执行。是在执行完finally语句后再通过return退出。
异常处理中throws和throw的区别
常用工具类
基本数据类型和包装类
1)八个基本数据类型的包装类
基本数据类型 包装类
byte Byte
boolean Boolean
short Short
char Character
int Integer
long Long
float Float
double Double
list.add()
2)为什么为基本类型引入包装类
基本数据类型有方便之处,简单、高效。
但是Java中的基本数据类型却是不面向对象的(没有属性、方法),这在实际使用时存在很多的不便(比如集合的元素只能是Object)。
为了解决这个不足,在设计类时为每个基本数据类型设计了一个对应的类进行包装,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class)。
包装类------ wrapperInstance.xxxValue() ----------->基本数据类型
包装类<-----new WrapperClass(primitive) new WrapperClass(string)------基本数据类型
4)自动装箱和自动拆箱
JDK1.5提供了自动装箱(autoboxing)和自动拆箱(autounboxing)功能, 从而实现了包装类和基本数据类型之间的自动转换
5)、包装类还可以实现基本类型变量和字符串之间的转换
基本类型变量------------String.valueof()------------>字符串
基本类型变量<------------WrapperClass.parseXxx(string)------------字符串
Integer与int的区别
int是java提供的8种原始数据类型之一。Java为每个原始类型提供了封装类,Integer是java为int提供的封装类。int的默认值为0,而Integer的默认值为null,即Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况,例如,要想表达出没有参加考试和考试成绩为0的区别,则只能使用Integer。在JSP开发中,Integer的默认为null,所以用el表达式在文本框中显示时,值为空白字符串,而int默认的默认值为0,所以用el表达式在文本框中显示时,结果为0,所以,int不适合作为web层的表单数据的类型。在Hibernate中,如果将OID定义为
另外,Integer提供了多个与整数相关的操作方法,例如,将一个字符串转换成整数,Integer中还定义了表示整数的最大值和最小值的常量。
String类为什么是final的
String、StringBuffer、StringBuilder区别与联系
String类型是基本数据类型吗?基本数据类型有哪些
基本数据类型包括byte、int、char、long、float、double、boolean和short。
java.lang.String类是引用数据类型,并且是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类
String s=“Hello”;s=s+“world!”;执行后,s内容是否改变?
没有。因为String被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。在这段代码中,s原先指向一个String对象,内容是 “Hello”,然后我们对s进行了+操作,那么s所指向的那个对象是否发生了改变呢?答案是没有。这时,s不指向原来那个对象了,而指向了另一个 String对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是s这个引用变量不再指向它了。 通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修改,那么使用String来代表字符串的话会引起很大的内存开销。因为 String对象建立之后不能再改变,所以对于每一个不同的字符串,都需要一个String对象来表示。这时,应该考虑使用StringBuffer类,它允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。 同时,我们还可以知道,如果要使用内容相同的字符串,不必每次都new一个String。例如我们要在构造器中对一个名叫s的String引用变量进行初始化,把它设置为初始值,应当这样做: public class Demo { private String s; … public Demo { s = “Initial Value”; } … } 而非 s = new String(“Initial Value”); 后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为String对象不可改变,所以对于内容相同的字符串,只要一个String对象来表示就可以了。也就说,多次调用上面的构造器创建多个对象,他们的String类型属性s都指向同一个对象。 上面的结论还基于这样一个事实:对于字符串常量,如果内容相同,Java认为它们代表同一个String对象。而用关键字new调用构造器,总是会创建一个新的对象,无论内容是否相同。 至于为什么要把String类设计成不可变类,是它的用途决定的。其实不只String,很多Java标准类库中的类都是不可变的。在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以Java标准类库还提供了一个可变版本,即 StringBuffer。
String s = new String(“xyz”);创建几个String Object?
两个或一个,”xyz”对应一个对象,这个对象放在字符串常量缓冲区,常量”xyz”不管出现多少遍,都是缓冲区中的那一个。New String每写一遍,就创建一个新的对象,它一句那个常量”xyz”对象的内容来创建出一个新String对象。如果以前就用过’xyz’,这句代表就不会创建”xyz”自己了,直接从缓冲区拿。
下面这条语句一共创建了多少个对象:String s=“a”+“b”+“c”+“d”;
答:对于如下代码:
String s1 = “a”;
String s2 = s1 + “b”;
String s3 = “a” + “b”;
System.out.println(s2 == “ab”);
System.out.println(s3 == “ab”);
第一条语句打印的结果为false,第二条语句打印的结果为true,这说明javac编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。
题目中的第一行代码被编译器在编译时优化后,相当于直接定义了一个”abcd”的字符串,所以,上面的代码应该只创建了一个String对象。写如下两行代码,
String s = “a” + “b” + “c” + “d”;
System.out.println(s == “abcd”);
最终打印的结果应该为true。
java.sql.Date和java.util.Date的联系和区别
java.sql.Date是java.util.Date的子类,是一个包装了毫秒值的瘦包装器,允许 JDBC 将毫秒值标识为 SQL DATE 值。毫秒值表示自 1970 年 1 月 1 日 00:00:00 GMT 以来经过的毫秒数。 为了与 SQL DATE 的定义一致,由 java.sql.Date 实例包装的毫秒值必须通过将时间、分钟、秒和毫秒设置为与该实例相关的特定时区中的零来“规范化”。 说白了,java.sql.Date就是与数据库Date相对应的一个类型,而java.util.Date是纯java的Date。
JAVA里提供的日期和时间类,java.sql.Date和java.sql.Time,只会从数据库里读取某部分值,这有时会导致丢失数据。例如一个包含2002/05/22 5:00:57 PM的字段,读取日期时得到的是2002/05/22,而读取时间时得到的是5:00:57 PM. 你需要了解数据库里存储时间的精度。有些数据库,比如MySQL,精度为毫秒,然而另一些数据库,包括Oracle,存储SQL DATE类型数据时,毫秒部分的数据是不保存的。以下操作中容易出现不易被发现的BUG:获得一个JAVA里的日期对象。 从数据库里读取日期 试图比较两个日期对象是否相等。如果毫秒部分丢失,本来认为相等的两个日期对象用Equals方法可能返回false。.sql.Timestamp类比java.util.Date类精确度要高。这个类包了一个getTime()方法,但是它不会返回额外精度部分的数据,因此必须使用…
总之,java.util.Date 就是Java的日期对象,而java.sql.Date 是针对SQL语句使用的,只包含日期而没有时间部分。
使用递归算法输出某个目录下所有文件和子目录列表
import java.io.File;
public class $ {
public static void main(String[] args) {
String path = “D:/301SXT”;
test(path);
}
private static void test(String path) {
File f = new File(path);
File[] fs = f.listFiles();
if (fs == null) {
return;
}
for (File file : fs) {
if (file.isFile()) {
System.out.println(file.getPath());
} else {
test(file.getPath());
}
}
}
}
第八章 集合
Java集合体系结构(List、Set、Collection、Map的区别和联系)
Collection 接口存储一组不唯一,无序的对象
List 接口存储一组不唯一,有序(插入顺序)的对象
Set 接口存储一组唯一,无序的对象
Map接口存储一组键值对象,提供key到value的映射。Key无序,唯一。value不要求有序,允许重复。(如果只使用key存储,而不使用value,那就是Set)
Vector和ArrayList的区别和联系
Vector和ArrayList的区别和联系
实现原理相同,功能相同,都是长度可变的数组结构,很多情况下可以互用
两者的主要区别如下
ArrayList和LinkedList的区别和联系
两者都实现了List接口,都具有List中元素有序、不唯一的特点。
ArrayList实现了长度可变的数组,在内存中分配连续空间。遍历元素和随机访问元素的效率比较高;
LinkedList采用链表存储方式。插入、删除元素时效率比较高
HashMap和Hashtable的区别和联系
实现原理相同,功能相同,底层都是哈希表结构,查询速度快,在很多情况下可以互用
两者的主要区别如下
HashSet的使用和原理(hashCode()和equals())
TreeSet的原理和使用(Comparable和comparator)
集合和数组的比较(为什么引入集合)
数组不是面向对象的,存在明显的缺陷,集合完全弥补了数组的一些缺点,比数组更灵活更实用,可大大提高软件的开发效率而且不同的集合框架类可适用于不同场合。具体如下:
Collection和Collections的区别
Collection是Java提供的集合接口,存储一组不唯一,无序的对象。它有两个子接口List和Set。
Java中还有一个Collections类,专门用来操作集合类 ,它提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。
IO流
输入流和输出流联系和区别,节点流和处理流联系和区别
首先,你要明白什么是“流”。直观地讲,流就像管道一样,在程序和文件之间,输入输出的方向是针对程序而言,向程序中读入东西,就是输入流,从程序中向外读东西,就是输出流。输入流是得到数据,输出流是输出数据。
而节点流,处理流是流的另一种划分,按照功能不同进行的划分。节点流,可以从或向一个特定的地方(节点)读写数据。处理流是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。
字符流字节流联系区别;什么时候使用字节流和字符流?
字符流和字节流是流的一种划分,按处理照流的数据单位进行的划分。两类都分为输入和输出操作。在字节流中输出数据主要是使用OutputStream完成,输入使的是InputStream,在字符流中输出主要是使用Writer类完成,输入流主要使用Reader类完成。这四个都是抽象类。字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组。字节流是最基本的,所有的InputStrem和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的 但实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的编码来处理,也就是要进行字符集的转化 这两个之间通过 InputStreamReader,OutputStreamWriter来关联,实际上是通过byte[]和String来关联的。
列举常用字节输入流和输出流并说明其特点,至少5对。
FileInputStream 从文件系统中的某个文件中获得输入字节。
ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read 方法要提供的下一个字节。
FilterInputStream 包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。FilterInputStream 类本身只是简单地重写那些将所有请求传递给所包含输入流的 InputStream 的所有方法。FilterInputStream 的子类可进一步重写这些方法中的一些方法,并且还可以提供一些额外的方法和字段。
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时,可以为应用程序提供对对象图形的持久存储。ObjectInputStream 用于恢复那些以前序列化的对象。其他用途包括使用套接字流在主机之间传递对象,或者用于编组和解组远程通信系统中的实参和形参。
StringBufferInputStream此类允许应用程序创建输入流,在该流中读取的字节由字符串内容提供。应用程序还可以使用ByteArrayInputStream 从 byte 数组中读取字节。 只有字符串中每个字符的低八位可以由此类使用。
ByteArrayOutputStream此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。
FileOutputStream文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流。文件是否可用或能否可以被创建取决于基础平台。特别是某些平台一次只允许一个 FileOutputStream(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。
FilterOutputStream类是过滤输出流的所有类的超类。这些流位于已存在的输出流(基础 输出流)之上,它们将已存在的输出流作为其基本数据接收器,但可能直接传输数据或提供一些额外的功能。 FilterOutputStream 类本身只是简单地重写那些将所有请求传递给所包含输出流的 OutputStream 的所有方法。FilterOutputStream 的子类可进一步地重写这些方法中的一些方法,并且还可以提供一些额外的方法和字段。
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。
PipedOutputStream可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,数据由某个线程写入 PipedOutputStream 对象,并由其他线程从连接的 PipedInputStream 读取。不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。如果某个线程正从连接的管道输入流中读取数据字节,但该线程不再处于活动状态,则该管道被视为处于毁坏状态。
说明缓冲流的优点和原理
不带缓冲的流的工作原理:它读取到一个字节/字符,就向用户指定的路径写出去,读一个写一个,所以就慢了。带缓冲的流的工作原理:读取到一个字节/字符,先不输出,等凑足了缓冲的最大容量后一次性写出去,从而提高了工作效率
优点:减少对硬盘的读取次数,降低对硬盘的损耗。
序列化的定义、实现和注意事项
想把一个对象写在硬盘上或者网络上,对其进行序列化,把他序列化成为一个字节流。
实现和注意事项:
使用IO流完成文件夹复制(结合递归)
public class MyTest2 {
public static void main(String[] args) throws IOException {
//复制多级文件夹
/* A:
案例演示:
需求:
复制D:\\course这文件夹到E:\\course
- 分析:
-a:封装D:\\course为一个File对象
- b:封装E:\\course为一个File对象, 然后判断是否存在, 如果不存在就是创建一个目录
- c:获取a中的File对应的路径下所有的文件对应的File数组
- d:遍历数组, 获取每一个元素, 进行复制
- e:释放资源*/
File srcFolder = new File("C:\\Users\\Desktop\\demo");
System.out.println(srcFolder.getName());
File targetFolder = new File("E:\\" + srcFolder.getName());
//如果目标文件夹不存在,就把他创建出来
if (!targetFolder.exists()) {
targetFolder.mkdirs();
}
//进行复制
copyFolder(srcFolder,targetFolder);
System.out.println("复制完成");
}
private static void copyFolder(File srcFolder, File targetFolder) throws IOException {
//遍历源文件夹,把源文件夹里面的文件,复制到目标文件夹里面去
File[] files = srcFolder.listFiles();
for (File f : files) {
if (f.isFile()) {
//进行复制
copyFiles(f, targetFolder);
}else{
//是子文件夹 递归调用
//1.封装源子文件夹 f
//System.out.println(f);
//2.封装目标子文件夹
File mbFolder = new File(targetFolder, f.getName());
if (!mbFolder.exists()) {
mbFolder.mkdirs();
}
//递归
copyFolder(f,mbFolder);
}
}
}
private static void copyFiles(File f, File targetFolder) throws IOException {
FileInputStream in = new FileInputStream(f);
//封装目标文件 targetFolder是父路径 f.getName()是子路径,相当于拼接了原来的目标文件的路径和新复制的文件的文件名 生成一个新的文件路径
File mbFile = new File(targetFolder,f.getName());
FileOutputStream out = new FileOutputStream(mbFile);
byte[] bytes = new byte[1024 * 8];
int len=0;
while ((len=in.read(bytes))!=-1){
out.write(bytes,0,len);
out.flush();
}
in.close();
out.close();
}
}
第十章 多线程
进程和线程有什么联系和区别?
1.定义:
2.进程和线程的关系:
(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
(2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。
(3)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
(4)处理机分给线程,即真正在处理机上运行的是线程。
(5)线程是指进程内的一个执行单元,也是进程内的可调度实体。
3.线程与进程的区别:
(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位。
(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可以并发执行。
(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。
(4)系统开销:在创建或撤销进程的时候,由于系统都要为之分配和回收资源,导致系统的明显大于创建或撤销线程时的开销。但进程有独立的地址空间,进程崩溃后,在保护模式下不会对其他的进程产生影响,而线程只是一个进程中的不同的执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但是在进程切换时,耗费的资源较大,效率要差些
创建线程的两种方式分别是什么,优缺点是什么?
方式1:继承Java.lang.Thread类,并覆盖run() 方法。优势:编写简单;劣势:无法继承其它父类
public class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name);
}
//run方法就是让线程执行的方法
@Override
public void run() {
for (int i = 0; i < 100; i++) {
String name = Thread.currentThread().getName();
System.out.println(name+"==="+i);
}
}
}
public class MyTest {
public static void main(String[] args) {
MyThread th1 = new MyThread();
MyThread th2 = new MyThread();
th1.setPriority(1);
th2.setPriority(10);
th1.setName("A线程");
th2.setName("B线程");
th1.start();
th2.start();
}
}
方式2:实现Java.lang.Runnable接口,并实现run()方法。优势:可继承其它类,多线程可共享同一个Thread对象;劣势:编程方式稍微复杂,如需访问当前线程,需调用Thread.currentThread()方法
public class MyRunnable implements Runnable{
@Override
public void run() {
//不是线程类 故没有this.getName()方法
for (int i = 0; i < 100; i++) {
Thread th = Thread.currentThread();
System.out.println(th.getName()+"==="+i);
}
}
}
public class MyTest {
public static void main(String[] args) {
// Runnable 任务 接口应该由那些打算通过某一线程执行其实例的类来实现。
MyRunnable myRunnable = new MyRunnable();
Thread th1 = new Thread(myRunnable);
th1.setName("A线程");
th1.start();
MyRunable2 myRunable2 = new MyRunable2();
Thread th2 = new Thread(myRunable2);
th2.setName("B线程");
th2.start();
}
}
方式3:实现Java.lang.Callable接口,并实现run()方法
public class MyCallable implements Callable<Integer> {
private int num;
public MyCallable(int num) {
this.num=num;
}
@Override
public Integer call() throws Exception {
//求1-num之间的和
int sum=0;
for (int i = 1; i <= num; i++) {
sum+=i;
}
return sum;
}
}
public class MyTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable(100);
FutureTask<Integer> task = new FutureTask<>(myCallable);
Thread th = new Thread(task);
th.start();
Integer integer = task.get();
System.out.println(integer);
}
}
Java创建线程后,调用start()方法和run()的区别
两种方法的区别
1) start:
用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
2) run:
run()方法只是类的一个普通方法而已,如果直接调用run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待
run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。
这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。
两种方式的比较 :
实际中往往采用实现Runable接口,一方面因为java只支持单继承,继承了Thread类就无法再继续继承其它类,而且Runable接口只有一个run方法;另一方面通过结果可以看出实现Runable接口才是真正的多线程。
线程的生命周期
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
(1)生命周期的五种状态
新建(new Thread)
当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。
例如:Thread t1=new Thread();
就绪(runnable)
线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。例如:t1.start();
运行(running)
线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。
死亡(dead)
当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。
自然终止:正常运行run()方法后终止
异常终止:调用stop()方法让一个线程终止运行
堵塞(blocked)
由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。
正在睡眠:用sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。
正在等待:调用wait()方法。(调用motify()方法回到就绪状态)
被另一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)
如何实现线程同步?
当多个线程访问同一个数据时,容易出现线程安全问题,需要某种方式来确保资源在某一时刻只被一个线程使用。需要让线程同步,保证数据安全
线程同步的实现方案:同步代码块和同步方法,均需要使用synchronized关键字
同步代码块:public void makeWithdrawal(int amt) {
synchronized (acct) { }
}
同步方法:public synchronized void makeWithdrawal(int amt) { }
线程同步的好处:解决了线程安全问题
线程同步的缺点:性能下降,可能会带来死锁
关于同步锁的更多细节
Java中每个对象都有一个内置锁。
当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。
当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。
一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。
释放锁是指持锁线程退出了synchronized同步方法或代码块。
关于锁和同步,有一下几个要点:
1)、只能同步方法,而不能同步变量和类;
2)、每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步?
3)、不必同步类中所有的方法,类可以同时拥有同步和非同步方法。
4)、如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。
5)、如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。
6)、线程睡眠时,它所持的任何锁都不会释放。
7)、线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。
8)、同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。
9)、在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。例如:
public int fix(int y) {
synchronized (this) {
x = x - y;
}
return x;
}
当然,同步方法也可以改写为非同步方法,但功能完全一样的,例如:
public synchronized int getX() {
return x++;
}
与
public int getX() {
synchronized (this) {
return x;
}
}
效果是完全一样的。
简述sleep( )和wait( )有什么区别?
sleep()是让某个线程暂停运行一段时间,其控制范围是由当前线程决定,也就是说,在线程里面决定.好比如说,我要做的事情是 “点火->烧水->煮面”,而当我点完火之后我不立即烧水,我要休息一段时间再烧.对于运行的主动权是由我的流程来控制。
而wait(),首先,这是由某个确定的对象来调用的,将这个对象理解成一个传话的人,当这个人在某个线程里面说"暂停!",也是 thisObj.wait(),这里的暂停是阻塞,还是"点火->烧水->煮饭",thisObj就好比一个监督我的人站在我旁边,本来该线 程应该执行1后执行2,再执行3,而在2处被那个对象喊暂停,那么我就会一直等在这里而不执行3,但正个流程并没有结束,我一直想去煮饭,但还没被允许, 直到那个对象在某个地方说"通知暂停的线程启动!",也就是thisObj.notify()的时候,那么我就可以煮饭了,这个被暂停的线程就会从暂停处 继续执行。 其实两者都可以让线程暂停一段时间,但是本质的区别是一个线程的运行状态控制,一个是线程之间的通讯的问题
Java中实现线程通信的三个方法的作用是什么?
Java提供了3个方法解决线程之间的通信问题,均是java.lang.Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常。
方法名 | 作 用 |
---|---|
final void wait() | 表示线程一直等待,直到其它线程通知 |
void wait(long timeout) | 线程等待指定毫秒参数的时间 |
final void wait(long timeout,int nanos) | 线程等待指定毫秒、微妙的时间 |
final void notify() | 唤醒一个处于等待状态的线程。注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。 |
final void notifyAll() | 唤醒同一个对象上所有调用wait()方法的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争 |
网络编程
IP地址和端口号
IP地址
IP地址分类
特殊的IP地址
端口:port
端口分类
理解IP和端口的关系
介绍OSI七层模型和TCP/IP模型
TCP协议和UDP协议的比较
TCP和UDP是TCP/IP协议栈中传输层的两个协议,它们使用IP路由功能把数据包发送到目的地,从而为应用程序及应用层协议(包括:HTTP、SMTP、SNMP、FTP和Telnet)提供网络服务。
TCP的server和client之间通信就好比两个人打电话,需要互相知道对方的电话号码,然后开始对话。所以在两者的连接过程中间需要指定端口和地址。
UDP的server和client之间的通信就像两个人互相发信。我只需要知道对方的地址,然后就发信过去。对方是否收到我不知道,也不需要专门对口令似的来建立连接。具体区别如下:
什么是Socket编程
什么是Socket编程
所谓socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。应用程序通常通过"套接字"向网络发出请求或者应答网络请求。
我们开发的网络应用程序位于应用层,TCP和UDP属于传输层协议,在应用层如何使用传输层的服务呢?在应用层和传输层之间,则是使用套接字来进行分离。
套接字就像是传输层为应用层开的一个小口,应用程序通过这个小口向远程发送数据,或者接收远程发来的数据;而这个小口以内,也就是数据进入这个口之后,或者数据从这个口出来之前,是不知道也不需要知道的,也不会关心它如何传输,这属于网络其它层次的工作。
Socket实际是传输层供给应用层的编程接口。传输层则在网络层的基础上提供进程到进程问的逻辑通道,而应用层的进程则利用传输层向另一台主机的某一进程通信。Socket就是应用层与传输层之间的桥梁
使用Socket编程可以开发客户机和服务器应用程序,可以在本地网络上进行通信,也可通过Internet在全球范围内通信。
生活案例1如果你想写封邮件发给远方的朋友,如何写信、将信打包,属于应用层。信怎么写,怎么打包完全由我们做主;而当我们将信投入邮筒时,邮筒的那个口就是套接字,在进入套接字之后,就是传输层、网络层等(邮局、公路交管或者航线等)其它层次的工作了。我们从来不会去关心信是如何从西安发往北京的,我们只知道写好了投入邮筒就OK了。
生活案例2:可以把Socket比作是一个港口码头,应用程序只要将数据交给Socket,就算完成了数据的发送,具体细节由Socket来完成,细节不必了解。同理,对于接收方,应用程序也要创建一个码头,等待数据的到达,并获取数据。
简述基于TCP和UDP的Socket编程的主要步骤
Java分别为TCP和UDP 两种通信协议提供了相应的Socket编程类,这些类存放在java.net包中。与TCP对应的是服务器的ServerSocket和客户端的Socket,与UDP对应的是DatagramSocket。
基于TCP创建的套接字可以叫做流套接字,服务器端相当于一个监听器,用来监听端口。 服务器与客服端之间的通讯都是输入输出流来实现的。基于UDP的套接字就是数据报套接字,• 两个都要先构造好相应的数据包。
基于TCP协议的Socket编程的主要步骤
服务器端(server):
\1. 构建一个ServerSocket实例,指定本地的端口。这个socket就是用来监听指定端口的连接请求的。
2.重复如下几个步骤:
a. 调用socket的accept()方法来获得下面客户端的连接请求。通过accept()方法返回的socket实例,建立了一个和客户端的新连接。
b.通过这个返回的socket实例获取InputStream和OutputStream,可以通过这两个stream来分别读和写数据。
c.结束的时候调用socket实例的close()方法关闭socket连接。
客户端(client):
1.构建Socket实例,通过指定的远程服务器地址和端口来建立连接。
2.通过Socket实例包含的InputStream和OutputStream来进行数据的读写。
3.操作结束后调用socket实例的close方法,关闭。
2.2 UDP
服务器端(server):
\1. 构造DatagramSocket实例,指定本地端口。
\2. 通过DatagramSocket实例的receive方法接收DatagramPacket.DatagramPacket中间就包含了通信的内容。
\3. 通过DatagramSocket的send和receive方法来收和发DatagramPacket.
客户端(client):
\1. 构造DatagramSocket实例。
2.通过DatagramSocket实例的send和receive方法发送DatagramPacket报文。
3.结束后,调用DatagramSocket的close方法关闭。
第十二章 反射技术
Java反射技术主要实现类有哪些,作用分别是什么?
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中
Class类的作用?生成Class对象的方法有哪些?
Class类是Java 反射机制的起源和入口,用于获取与类相关的各种信息,提供了获取类信息的相关方法。Class类继承自Object类
Class类是所有类的共同的图纸。每个类有自己的对象,好比图纸和实物的关系;每个类也可看做是一个对象,有共同的图纸Class,存放类的结构信息,能够通过相应方法取出相应信息:类的名字、属性、方法、构造方法、父类和接口
方 法 | 示 例 |
---|---|
对象名.getClass() | String str=“bdqn”; Class clazz = str.getClass(); |
对象名.getSuperClass() | Student stu = new Student(); Class c1 = stu.getClass(); Class c2 = stu.getSuperClass(); |
Class.forName() | Class clazz = Class.forName(“java.lang.Object”); Class.forName(“oracle.jdbc.driver.OracleDriver”); |
类名.class | Class c1 = String.class; Class c2 = Student.class; Class c2 = int.class |
包装类.TYPE | Class c1 = Integer.TYPE; Class c2 = Boolean.TYPE; |
反射的使用场合和作用、及其优缺点
使用场合:在编译时根本无法知道该对象或类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息。
主要作用:通过反射可以使程序代码访问装载到JVM 中的类的内部信息
获取已装载类的属性信息
获取已装载类的方法
获取已装载类的构造方法信息
反射的优点
反射提高了Java程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类;反射是其它一些常用语言,如C、C++、Fortran 或者Pascal等都不具备的
Java反射技术应用领域很广,如软件测试、 EJB、JavaBean等;许多流行的开源框架例如Struts、Hibernate、Spring在实现过程中都采用了该技术
性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此Java反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。
使用反射会模糊程序内部逻辑:程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。
第十三章 设计模式入门
什么是设计模式,设计模式的作用。
设计模式是一套被反复使用的、多数人知晓、经过分类编目的优秀代码设计经验的总结。特定环境下特定问题的处理方法。
23种经典设计模式都有哪些,如何分类。
写出简单工厂模式的示例代码
public abstract class Animal {
public abstract void eat();
}
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
public class Tiger extends Animal{
@Override
public void eat() {
System.out.println("老虎吃鸡");
}
}
public class AnimalFactory {
private AnimalFactory() {
}
public static Animal getAnimal(String name){
if ("cat".equals(name)){
return new Cat();
}else if ("dog".equals(name)){
return new Dog();
}else if ("tiger".equals(name)){
return new Tiger();
}else {
return null;
}
}
}
public class MyTest {
public static void main(String[] args) {
Animal animal = AnimalFactory.getAnimal("cat");
animal.eat();
animal=AnimalFactory.getAnimal("dog");
animal.eat();
animal=AnimalFactory.getAnimal("tiger");
animal.eat();
}
}
基本原理:由一个工厂类根据传入的参数(一般是字符串参数),动态决定应该创建哪一个产品子类(这些产品子类继承自同一个父类或接口)的实例,并以父类形式返回
优点:客户端不负责对象的创建,而是由专门的工厂类完成;客户端只负责对象的调用,实现了创建和调用的分离,降低了客户端代码的难度;
缺点:如果增加和减少产品子类,需要修改简单工厂类,违背了开闭原则;如果产品子类过多,会导致工厂类非常的庞大,违反了高内聚原则,不利于后期维护
写出单例模式的示例代码
/**
* 饿汉式的单例模式
* 在类加载的时候创建单例实例,而不是等到第一次请求实例的时候的时候创建
* 1、私有 的无参数构造方法Singleton(),避免外部创建实例
* 2、私有静态属性instance
* 3、公有静态方法getInstance()
*/
public class Student {
private static Student student=new Student();
private Student() {
}
public static Student getStudent(){
return student;
}
}
public class MyTest {
public static void main(String[] args) {
Student student = Student.getStudent();
Student student1 = Student.getStudent();
System.out.println(student==student1);
}
}
/**
* 懒汉式的单例模式
*在类加载的时候不创建单例实例,只有在第一次请求实例的时候的时候创建
*/
public class Student {
private static Student student=null;
private Student() {
}
public static Student getStudent(){
if (student==null){
student=new Student();
}
return student;
}
}
public class MyTest {
public static void main(String[] args) {
Student student = Student.getStudent();
Student student1 = Student.getStudent();
System.out.println(student==student1);
}
}
请对你所熟悉的一个设计模式进行介绍
分析:建议挑选有一定技术难度,并且在实际开发中应用较多的设计模式。可以挑选装饰模式和动态代理模式。此处挑选动态代理设计模式。
讲解思路:生活案例引入、技术讲解、优缺点分析、典型应用。
1、生活案例引入:送生日蛋糕:
MM们要过生日了,怎么也得表示下吧。最起码先送个蛋糕。蛋糕多种多样了。巧克力,冰淇淋,奶油等等。这都是基本的了,再加点额外的装饰,如蛋糕里放点花、放贺卡、放点干果吃着更香等等。
分析:
方案1:如果采用继承会造成大量的蛋糕子类
方案2、蛋糕作为主体,花,贺卡,果仁等是装饰者,需要时加到蛋糕上。要啥我就加啥。
装饰模式(别名Wrapper)是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它通过创建一个包装对象,也就是装饰来包裹真实对象,提供了比继承更具弹性的代替方案。
装饰模式一般涉及到的角色
3、优缺点分析
优点
缺点
符合的设计原则:
4、典型应用
java IO中需要完成对不同输入输出源的操作,如果单纯的使用继承这一方式,无疑需要很多的类。比如说,我们操作文件需要一个类,实现文件的字节读取需要一个类,实现文件的字符读取又需要一个类…一次类推每个特定的操作都需要一个特定的类。这无疑会导致大量的IO继承类的出现。显然对于编程是很不利的。
而是用装饰模式则可以很好的解决这一问题,在装饰模式中:节点流(如FileInputStream)直接与输入源交互,之后通过过滤流(FilterInputStream)进行装饰,这样获得的io对象便具有某几个的功能,很好的拓展了IO的功能。
dent1 = Student.getStudent();
System.out.println(student==student1);
}
}
/**
\* 懒汉式的单例模式
*在类加载的时候不创建单例实例,只有在第一次请求实例的时候的时候创建
*/
```java
public class Student {
private static Student student=null;
private Student() {
}
public static Student getStudent(){
if (student==null){
student=new Student();
}
return student;
}
}
public class MyTest {
public static void main(String[] args) {
Student student = Student.getStudent();
Student student1 = Student.getStudent();
System.out.println(student==student1);
}
}
请对你所熟悉的一个设计模式进行介绍
分析:建议挑选有一定技术难度,并且在实际开发中应用较多的设计模式。可以挑选装饰模式和动态代理模式。此处挑选动态代理设计模式。
讲解思路:生活案例引入、技术讲解、优缺点分析、典型应用。
1、生活案例引入:送生日蛋糕:
MM们要过生日了,怎么也得表示下吧。最起码先送个蛋糕。蛋糕多种多样了。巧克力,冰淇淋,奶油等等。这都是基本的了,再加点额外的装饰,如蛋糕里放点花、放贺卡、放点干果吃着更香等等。
分析:
方案1:如果采用继承会造成大量的蛋糕子类
方案2、蛋糕作为主体,花,贺卡,果仁等是装饰者,需要时加到蛋糕上。要啥我就加啥。
装饰模式(别名Wrapper)是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它通过创建一个包装对象,也就是装饰来包裹真实对象,提供了比继承更具弹性的代替方案。
装饰模式一般涉及到的角色
[外链图片转存中…(img-aTsKUmGN-1592451917871)]
3、优缺点分析
优点
缺点
符合的设计原则:
4、典型应用
java IO中需要完成对不同输入输出源的操作,如果单纯的使用继承这一方式,无疑需要很多的类。比如说,我们操作文件需要一个类,实现文件的字节读取需要一个类,实现文件的字符读取又需要一个类…一次类推每个特定的操作都需要一个特定的类。这无疑会导致大量的IO继承类的出现。显然对于编程是很不利的。
而是用装饰模式则可以很好的解决这一问题,在装饰模式中:节点流(如FileInputStream)直接与输入源交互,之后通过过滤流(FilterInputStream)进行装饰,这样获得的io对象便具有某几个的功能,很好的拓展了IO的功能。
[外链图片转存中…(img-3vuuzbAQ-1592451917872)]