javaSE 面试题总结

标题javaSE 面试题总结

标题第一章 初识 Java

Java 跨平台原理(字节码文件、虚拟机)

  1. C/C++语言都直接编译成针对特定平台机器码。如果要跨平台, 需要使用相应的编译器重新编译。
  2. Java 源程序(.java)要先编译成与平台无关的字节码文件 (.class),然后字节码文件再解释成机器码运行。解释是通过 Java 虚拟机来执行的。
  3. 字节码文件不面向任何具体平台,只面向虚拟机。
  4. Java 虚拟机是可运行 Java 字节码文件的虚拟计算机。不同平 台的虚拟机是不同的,但它们都提供了相同的接口。
  5. Java 语言具有一次编译,到处运行的特点。就是说编译后 的.class 可以跨平台运行,前提是该平台具有相应的 Java 虚拟机。 但是性能比 C/C++要低。
  6. Java 的跨平台原理决定了其性能没有 C/C++高 Java 的安全性 语言层次的安全性主要体现在: 1. Java 取消了强大但又危险的指针,而代之以引用。由于指针可 进行移动运算,指针可随便指向一个内存区域,而不管这个区域是否 可用,这样做是危险的,因为原来这个内存地址可能存储着重要数据 或者是其他程序运行所占用的,并且使用指针也容易数组越界。 2. 垃圾回收机制:不需要程序员直接控制内存回收,由垃圾回收 器在后台自动回收不再使用的内存。避免程序忘记及时回收,导致内 存泄露。避免程序错误回收程序核心类库的内存,导致系统崩溃。 3. 异常处理机制:Java 异常机制主要依赖于 try、catch、finally、 throw、throws 五个关键字。 4. 强制类型转换:只有在满足强制转换规则的情况下才能强转成 功。底层的安全性可以从以下方面来说明 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? 1. JVM :JVM 是 Java Virtual Machine(Java 虚拟机)的缩写, 它是整个 java 实现跨平台的最核心的部分,所有的 java 程序会 首先被编译为.class 的类文件,这种类文件可以在虚拟机上执行, 也就是说 class 并不直接与机器的操作系统相对应,而是经过虚 拟机间接与操作系统交互,由虚拟机将程序解释给本地系统执行。 JVM 是 Java 平台的基础,和实际的机器一样,它也有自己的指令 集,并且在运行时操作不同的内存区域。 JVM 通过抽象操作系 统和 CPU 结构,提供了一种与平台无关的代码执行方法,即与特 殊的实现方法、主机硬件、主机操作系统无关。JVM 的主要工作 是解释自己的指令集(即字节码)到 CPU 的指令集或对应的系统 调用,保护用户免被恶意程序骚扰。 JVM 对上层的 Java 源文件 是不关心的,它关注的只是由源文件生成的类文件(.class 文件)。 2. JRE:JRE 是 java runtime environment(java 运行环境)的 缩写。光有 JVM 还不能让 class 文件执行,因为在解释 class 的 时候 JVM 需要调用解释所需要的类库 lib。在 JDK 的安装目录里 你可以找到 jre 目录,里面有两个文件夹 bin 和 lib,在这里可以 认为 bin 里的就是 jvm,lib 中则是 jvm 工作所需要的类库,而 jvm 和 lib 和起来就称为 jre。所以,在你写完 java 程序编译 成.class 之后,你可以把这个.class 文件和 jre 一起打包发给朋 友,这样你的朋友就可以运行你写程序了(jre 里有运行.class
    的 java.exe)。JRE 是 Sun 公司发布的一个更大的系统,它里面 就有一个 JVM。JRE 就与具体的 CPU 结构和操作系统有关,是运行 Java 程序必不可少的(除非用其他一些编译环境编译成.exe 可执 行文件……),JRE 的地位就象一台 PC 机一样,我们写好的 Win32 应用程序需要操作系统帮我们运行,同样的,我们编写的 Java 程 序也必须要 JRE 才能运行。 3. JDK:JDK 是 java development kit(java 开发工具包)的缩 写。每个学 java 的人都会先在机器上装一个 JDK,那 让我们看 一下 JDK 的安装目录。在目录下面有六个文件夹、一个 src 类库 源码压缩包、和其他几个声明文件。其中,真正在运行 java 时起 作用的是以下四个文件夹:bin、include、lib、jre。现在我们 可以看出这样一个关系,JDK 包含 JRE,而 JRE 包含 JVM。 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 种注释类型。使用如下: 1. 单行注释,采用“//”方式.只能注释一行代码。如://类成员 变量 2. 多行注释,采用“//”方式,可注释多行代码,其中不允 许出现嵌套。如: /System.out.println(“a”); System.out.println(“b”); System.out.println(“c”);/ 1. 文档注释,采用“/…*/”方式。如: / * 子类 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。推介平时多使用&&,因为它 效率更高些。
5. 、&还可以用作位运算符。当&两边操作数或两边表达式的结果 不是 boolean 类型时,&用于按位与运算符的操作。 |和||的区别和联系与&和&&的区别和联系类似 用最有效率的方法算出 2 乘以 8 等于多少 使用位运算来实现效率最高。位运算符是对操作数以二进制比特 位为单位进行操作和运算,操作数和结果都是整型数。对于位运 算符“<<”, 是将一个数左移 n 位,就相当于乘以了 2 的 n 次方, 那么,一个数乘以 8 只要将其左移 3 位即可,位运算 cpu 直接支 持的,效率最高。所以,2 乘以 8 等于几的最效率的方法是 2 << 3。 基本数据类型的类型转换规则 基本类型转换分为自动转换和强制转换。 自动转换规则:容量小的数据类型可以自动转换成容量大的数据类 型,也可以说低级自动向高级转换。这儿的容量指的不是字节数,而 是指类型表述的范围。 强制转换规则:高级变为低级需要强制转换。
如何转换: (1)、赋值运算符“=”右边的转换,先自动转换成表达式中级别最 高的数据类型,再进行运算。 (2)、赋值运算符“=”两侧的转换,若左边级别>右边级别,会自 动转换;若左边级别 == 右边级别,不用转换;若左边级别 < 右边 级别,需强制转换。 (3)、可以将整型常量直接赋值给 byte, short, char 等类型变量, 而不需要进行强制类型转换,前提是不超出其表述范围,否则必须进 行强制转换。
第三章 流程控制 三种流程控制结构 其流程控制方式采用结构化程序设计中规定的三种基本流程结构, 即:顺序结构、分支结构和循环结构 if 多分支语句和 switch 多分支语句的异同之处 相同之处:都是分支语句,多超过一种的情况进行判断处理。 不同之处: 1. SWITCH 更适合用于多分支情况,就是有很多种情况需要判断处 理,判断条件类型单一,只有一个入口,在分支执行完后(如果没有 break 跳出),不加判断地执行下去;而 if—elseif—else 多分枝 主要适用于分支较少的分支结构,判断类型不是单一,只要一个分支 被执行后,后边的分支不再执行。 2. switch 为等值判断(不允许比如>= <=),而 if 为等值和区间 都可以,if 的使用范围大。 while 和 do-while 循环的区别 while 先判断后执行,第一次判断为 false,循环体一次都不执行 do while 先执行 后判断,最少执行 1 次。 如果 while 循环第一次判断为 true, 则两种循环没有区别。
break 和 continue 的作用 break: 结束当前循环并退出当前循环体。 break 还可以退出 switch 语句 continue: 循环体中后续的语句不执行,但是循环没有结束,继续进 行循环条件的判断(for 循环还会 i++)。continue 只是结束本次循 环。请使用递归算法计算 n! public class Test { public int factorial(int n) { if (n == 1 || n == 0){ return n; }else{ return n * factorial(n - 1); }}public static void main(String[] args) { Test test = new Test(); System.out.println(test.factorial(6)); }}
递归的定义和优缺点 递归算法是一种直接或者间接地调用自身算法的过程。在计算 机编写程序中,递归算法对解决一大类问题是十分有效的,它往 往使算法的描述简洁而且易于理解。 递归算法解决问题的特点: (1) 递归就是在过程或函数里调用自身。 (2) 在使用递归策略时,必须有一个明确的递归结束条件,称 为递归出口。 (3) 递归算法解题通常显得很简洁,但运行效率较低。所以一 般不提倡用递归算法设计程序。 (4) 在递归调用的过程当中系统为每一层的返回点、局部量等 开辟了栈来存储。递归次数过多容易造成栈溢出等。所以一般不 提倡用递归算法设计程序。
第四章 数组 数组的特征 1. 数组是(相同类型数据)的(有序)(集合) 2. 数组会在内存中开辟一块连续的空间,每个空间相当于之前的 一个变量,称为数组的元素 element 3. 元素的表示 数组名[下标或者索引] scores[7] scores[0] scores[9] 4. 索引从 0 开始 5. 每个数组元素有默认值 double 0.0 boolean false int 0 6. 数组元素有序的,不是大小顺序,是索引 的顺序 7. 数组中可以存储基本数据类型,可以存储引用数据类型;但是 对于一个数组而言,数组的类型是固定的,只能是一个 8. length:数组的长度 9. 数组的长度是固定的,一经定义,不能再发生变化(数组的扩 容)
冒泡排序代码 1. 冒泡排序算法 public class TestBubbleSort { public static void sort(int[] a) { int temp = 0; // 外层循环,它决定一共走几趟 for (int i = 0; i <a.length-1; ++i) { //内层循环,它决定每趟走一次 for (int j = 0; j <a.length-i-1 ; ++j) { //如果后一个大于前一个 if (a[j + 1] < a[j]) { //换位 temp = a[j];a[j] = a[j + 1];a[j + 1] = temp; }}}public static void sort2(int[] a) { int temp = 0; for (int i = 0; i <a.length-1; ++i) {
//通过符号位可以减少无谓的比较,如果已经有序了,就退出循 环int flag = 0; for (int j = 0; j <a.length-1-i ; ++j) { if (a[j + 1] < a[j]) { temp = a[j];a[j] = a[j + 1];a[j + 1] = temp; flag = 1; }}if(flag == 0){ break;} }}}选择排序的代码 public class TestSelectSort { public static void sort(int arr[]) { int temp = 0; for (int i = 0; i < arr.length - 1; i++) { // 认为目前的数就是最小的, 记录最小数的下标 int minIndex = i; for (int j = i + 1; j < arr.length; j++) { if (arr[minIndex] > arr[j]) {
// 修改最小值的下标 minIndex = j; }}// 当退出 for 就找到这次的最小值 if (i != minIndex) { temp = arr[i]; arr[i] = arr[minIndex]; arr[minIndex] = temp; }}}} 插入排序的代码 public class TestInsertSort { public static void sort(int arr[]) { int i, j; for (i = 1; i < arr.length; i++) { int temp = arr[i]; for (j = i; j > 0 && temp < arr[j - 1]; j–) { arr[j] = arr[j - 1];
}arr[j] = temp; }}} 可变参数的作用和特点 总结 1:可变参数 1.可变参数的形式 … 2.可变参数只能是方法的形参 3.可变参数对应的实参可以 0,1,2…个,也可以是一个数组 4.在可变参数的方法中,将可变参数当做数组来处理 5.可变参数最多有一个,只能是最后一个 6.可变参数好处:方便 简单 减少重载方法的数量 7.如果定义了可变参数的方法,不允许同时定义相同类型数组参数的 方法总结 2:数组做形参和可变参数做形参联系和区别 联系: 1.实参都可以是数组;2.方法体中,可变参数当做数组来处理 区别: 1.个数不同 可变参数只能有一个 数组参数可以多个 2.位置不同 可变参数只能是最后一个 数组参数位置任意
3.实参不同 可变参数实参可以 0,1,2…个,也可以是一个数组, 数组的实参只能是数组
第五章 面向对象 类和对象的关系 类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存, 而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一 个定义包括在特定类型的对象中的方法和变量的软件模板。 类和对象好比图纸和实物的关系,模具和铸件的关系。 比如人类就是一个概念,人类具有身高,体重等属性。人类可以做吃 饭、说话等方法。 小明就是一个具体的人,也就是实例,他的属性是具体的身高 200cm, 体重 180kg,他做的方法是具体的吃了一碗白米饭,说了“12345” 这样一句话。 面向过程和面向对象的区别 两者都是软件开发思想,先有面向过程,后有面向对象。在大型项目 中,针对面向过程的不足推出了面向对象开发思想。 比喻蒋介石和 Mao 泽东分别是面向过程和面向对象的杰出代表,这样充分 说明,在解决复制问题时,面向对象有更大的优越性。 面向过程是蛋炒饭,面向对象是盖浇饭。盖浇饭的好处就是“菜”“饭” 分离,从而提高了制作盖浇饭的灵活性。饭不满意就换饭,菜不满意
换菜。用软件工程的专业术语就是“可维护性”比较好,“饭” 和 “菜”的耦合度比较低。 区别 1. 编程思路不同: 面向过程以实现功能的函数开发为主,而面向 对象要首先抽象出类、属性及其方法,然后通过实例化类、执行方法 来完成功能。 2. 封装性:都具有封装性,但是面向过程是封装的是功能,而面 向对象封装的是数据和功能。 3. 面向对象具有继承性和多态性,而面向过程没有继承性和多态 性,所以面向对象优势是明显。 方法重载和方法重写(覆盖)的区别 英文 位置不 同 作用不同 重载 overload 同一个 类中 在一个类里面为一种行为提供多种 实现方式并提高可读性 重写 override 子类和 父类间 父类方法无法满足子类的要求,子类 通过方法重写满足要求 修饰符 返回值 方法名 参数 抛出异常
重载 无关 无关 相同 不同 无关 重写 大于等于 小于等于 相同 相同 小于等于 this 和 super 关键字的作用 this 是对象内部指代自身的引用 1. this 可以调用成员变量,通常用于解决成员变量和局部变量同 名冲突 2. this 可以调用成员方法 3. this 可以在构造方法中调用重载的构造方法,且必须是构造方 法的第一条语句。 super 代表对当前对象的直接父类对象的引用 1. super 可以调用直接父类的成员变量(注意权限修饰符的影响, 比如不能访问 private 成员) 2. super 可以调用直接父类的成员方法(注意权限修饰符的影响, 比如不能访问 private 成员) 3. super 可以调用直接父类的构造方法,只限构造方法中使用, 且必须是第一条语句。 static 关键字的作用(修饰变量、方法、代码块) static 可以修饰变量、方法、代码块和内部类
6. 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);
7. static 方法也可以通过对象名.方法名和类名.方法名两种方 式来访问 2. static 代码块。当类被第一次使用时(可能是调用 static 属 性和方法,或者创建其对象)执行静态代码块,且只被执行一次, 主要作用是实现 static 属性的初始化。 3. static 内部类:属于整个外部类,而不是属于外部类的每个对 象。不能访问外部类的非静态成员(变量或者方法),.可以访问 外部类的静态成员 final 和 abstract 关键字的作用 final 和 abstract 是功能相反的两个关键字,可以对比记忆 1. abstract 可以用来修饰类和方法,不能用来修饰属性和构造方 法;使用 abstract 修饰的类是抽象类,需要被继承,使用 abstract 修饰的方法是抽象方法,需要子类被重写。 2. final 可以用来修饰类、方法和属性,不能修饰构造方法。使 用 final 修饰的类不能被继承,使用 final 修饰的方法不能被重 写,使用 final 修饰的变量的值不能被修改,所以就成了常量。 3. 特别注意:final 修饰基本类型变量,其值不能改变。但是 final 修饰引用类型变量,栈内存中的引用不能改变,但是所指向的堆 内存中的对象的属性值仍旧可以改变。例如 class Test {
public static void main(String[] args) { final Dog dog = new Dog(“欧欧”); dog.name = “美美”;//正确 dog = new Dog(“亚亚”);//错误 }}final、finally、finalize 的区别 1. final 修饰符(关键字)如果一个类被声明为 final,意味着它 不能再派生出新的子类,不能作为父类被继承。将变量或方法声明为 final,可以保证它们在使用中不被改变。被声明为 final 的变量必 须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声 明为 final 的方法也同样只能使用,不能重载。 2. finally 在异常处理时提供 finally 块来执行任何清除操作。 如果有 finally 的话,则不管是否发生异常,finally 语句都会被执 行。 3. finalize 方法名。Java 技术允许使用 finalize() 方法在垃 圾收集器将对象从内存中清除出去之前做必要清理工作。finalize() 方法是在垃圾收集器删除对象之前被调用的。它是在 Object 类中定
义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理 系统资源或者执行其他清理工作。 写出 java.lang.Object 类的六个常用方法 1. public boolean equals(java.lang.Object) 比较内容 2. public native int hashCode() 哈希码 3. public java.lang.String toString() 变成字符串 4. public final native java.lang.Class getClass() 获取类结 构信息 1. protected void finalize() throws java.lang.Throwable 垃 圾回收前执行的方法 2. protected native Object clone() throws java.lang.CloneNotSupportedException 克隆 1. public final void wait() throws java.lang.InterruptedException 多线程中等待功能 2. public final native void notify() 多线程中唤醒功能 3. public final native void notifyAll() 多线程中唤醒所有等 待线程的功能
private/默认/protected/public 权限修饰符的区别 访问控制 public protected 默认 private 同一类中成员 是 是 是 是 同一包中其它类 是 是 是 不同包中的子类 是 是 不同包中对非子类 是 类的访问权限只有两种 public 公共的 可被同一项目中所有的类访问。 (必须与文件名 同名) default 默认的 可被同一个包中的类访问。 成员(成员变量或成员方法)访问权限共有四种: public 公共的 可以被项目中所有的类访问。(项目可见性) protected 受保护的 可以被这个类本身访问;同一个包中的所有 其他的类访问;被它的子类(同一个包以及不同包中的子类)访 问。(子类可见性) default 默认的 被这个类本身访问;被同一个包中的类访问。(包 可见性) private 私有的 只能被这个类本身访问。(类可见性)
继承条件下构造方法的执行过程 继承条件下构造方法的调用规则如下: 如果子类的构造方法中没有通过 super 显式调用父类的有参构 造方法,也没有通过 this 显式调用自身的其他构造方法,则系 统会默认先调用父类的无参构造方法。在这种情况下,写不写 “super();”语句,效果是一样的。 如果子类的构造方法中通过 super 显式调用父类的有参构造方 法,那将执行父类相应构造方法,而不执行父类无参构造方法。 如果子类的构造方法中通过 this 显式调用自身的其他构造方 法,在相应构造方法中应用以上两条规则。 特别注意的是,如果存在多级继承关系,在创建一个子类对象 时,以上规则会多次向更高一级父类应用,一直到执行顶级父 类 Object 类的无参构造方法为止。 ==和 equals 的区别和联系 :a) 基本类型,比较的是值 b) 引用类型,比较的是地址 c) 不能比较没有父子关系的两个对象 equals() a) 系统类一般已经覆盖了 equals(),比较的是内容。
b) 用户自定义类如果没有覆盖 equals(),将调用父类的 equals(比如是 Object),而 Object 的 equals 的比较是地 址(return (this == obj);) c) 用户自定义类需要覆盖父类的 equals() 注意:Object 的
和 equals 比较的都是地址,作用相同 多态的技能点(前提条件,向上转型、向下转型) 实现多态的三个条件 1. 继承的存在;(继承是多态的基础,没有继承就没有多态) 2. 子类重写父类的方法。(多态下会调用子类重写后的方法) 3. 父类引用变量指向子类对象。(涉及子类到父类的类型转换) 向上转型 Student person = new Student() 1. 将一个父类的引用指向一个子类对象,成为向上转型,自动进 行类型转换。 2. 此时通过父类引用变量调用的方法是子类覆盖或继承父类的方 法,而不是父类的方法 3. 此时通过父类引用变量无法调用子类特有的方法 向下转型 Student stu = (Student)person; 1. 将一个指向子类对象的引用赋给一个子类的引用,成为向下转 型,此时必须进行强制类型转换。
8. 向下转型必须转换为父类引用指向的真实子类类型,,否则将 出现 ClassCastException,不是任意的强制转换 3. 向下转型时可以结合使用 instanceof 运算符进行强制类型转 换,比如出现转换异常。 接口和抽象类的异同之处 相同点 抽象类和接口均包含抽象方法,类必须实现所有的抽象方法,否则是 抽象类 抽象类和接口都不能实例化,他们位于继承树的顶端,用来被其他类 继承和实现 两者的区别主要体现在两方面:语法方面和设计理念方面 语法方面的区别是比较低层次的,非本质的,主要表现在: 接口中只能定义全局静态常量,不能定义变量。抽象类中可以定义常 量和变量。 接口中所有的方法都是全局抽象方法。抽象类中可以有 0 个、1 个或 多个,甚至全部都是抽象方法。 抽象类中可以有构造方法,但不能用来实例化,而在子类实例化是执 行,完成属于抽象类的初始化操作。接口中不能定义构造方法。
一个类只能有一个直接父类(可以是抽象类),但可以充实实现多个 接口。一个类使用 extends 来继承抽象类,使用 implements 来实现 接口。二者的主要区别还是在设计理念上,其决定了某些情况下到底使 用抽象类还是接口。 抽象类体现了一种继承关系,目的是复用代码,抽象类中定义了各个 子类的相同代码,可以认为父类是一个实现了部分功能的“中间产 品”,而子类是“最终产品”。父类和子类之间必须存在“is-a”的 关系,即父类和子类在概念本质上应该是相同的。 接口并不要求实现类和接口在概念本质上一致的,仅仅是实现了接口 定义的约定或者能力而已。接口定义了“做什么”,而实现类负责完 成“怎么做”,体现了功能(规范)和实现分离的原则。接口和实现 之间可以认为是一种“has-a 的关系” 简述 Java 的垃圾回收机制 传统的 C/C++语言,需要程序员负责回收已经分配内存。显式 回收垃圾回收的缺点: 1. 程序忘记及时回收,从而导致内存泄露,降低系统性能。 2. 程序错误回收程序核心类库的内存,导致系统崩溃。 Java 语言不需要程序员直接控制内存回收,是由 JRE 在后台自 动回收不再使用的内存,称为垃圾回收机制。
9. 可以提高编程效率。 2. 保护程序的完整性。 3. 其开销影响性能。Java 虚拟机必须跟踪程序中有用的对象,确 定哪些是无用的。 垃圾回收机制的特点 1. 垃圾回收机制回收 JVM 堆内存里的对象空间,不负责回收栈内 存数据。 2. 对其他物理连接,比如数据库连接、输入流输出流、Socket 连 接无能为力。 3. 垃圾回收发生具有不可预知性,程序无法精确控制垃圾回收机 制执行。 4. 可以将对象的引用变量设置为 null,暗示垃圾回收机制可以回 收该对象。 5. 现在的 JVM 有多种垃圾回收实现算法,表现各异。 6. 垃圾回收机制回收任何对象之前,总会先调用它的 finalize 方法(如果覆盖该方法,让一个新的引用变量重新引用该对象,则会 重新激活对象)。
10. 程序员可以通过 System.gc()或者 Runtime.getRuntime().gc() 来通知系统进行垃圾回收,会有一些效果,但是系统是否进行垃圾回 收依然不确定。 8. 永远不要主动调用某个对象的 finalize 方法,应该交给垃圾回 收机制调用。
第六章 异常处理 Error 和 Exception 的区别 1. Error 类,表示仅靠程序本身无法恢复的严重错误,比如说内 存溢出、动态链接异常、虚拟机错误。应用程序不应该抛出这种类型 的对象。假如出现这种错误,除了尽力使程序安全退出外,在其他方 面是无能为力的。所以在进行程序设计时,应该更关注 Exception 类。 2. Exception 类,由 Java 应用程序抛出和处理的非严重错误,比 如所需文件没有找到、零作除数,数组下标越界等。它的各种不同子 类分别对应不同类型异常。可分为两类:Checked 异常和 Runtime 异 常
Checked 异常和 Runtime 异常的区别 1. 运行时异常:包括 RuntimeaException 及其所有子类。不要求 程 序 必 须 对 它 们 作 出 处 理 , 比 如 InputMismatchException 、 ArithmeticException、NullPointerException 等。即使没有使用 try-catch 或 throws 进行处理,仍旧可以进行编译和运行。如果运 行时发生异常,会输出异常的堆栈信息并中止程序执行。 2. Checked 异常(非运行时异常):除了运行时异常外的其他异 常类都是 Checked 异常。程序必须捕获或者声明抛出这种异常,否则 出现编译错误,无法通过编译。处理方式包括两种:通过 try-catch 捕获异常,通过 throws 声明抛出异常从而交给上一级调用方法处理。 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. 作用不同:throw 用于程序员自行产生并抛出异常;throws 用 于声明在该方法内抛出了异常 2. 使用的位置不同:throw 位于方法体内部,可以作为单独语句 使用;throws 必须跟在方法参数列表的后面,不能单独使用。 3. 内容不同:throw 抛出一个异常对象,且只能是一个;throws 后面跟异常类,而且可以有多个。
第七章 常用工具 基本数据类型和包装类 1)八个基本数据类型的包装类 基本数据类型 包装类 byte Byte boolean Boolean short Short char Character int Integer long Long float Float double Double list.add() 2)为什么为基本类型引入包装类 基本数据类型有方便之处,简单、高效。 但是 Java 中的基本数据类型却是不面向对象的(没有属性、 方法),这在实际使用时存在很多的不便(比如集合的元素只 能是 Object)。
为了解决这个不足,在设计类时为每个基本数据类型设计了一 个对应的类进行包装,这样八个和基本数据类型对应的类统称 为包装类(Wrapper Class)。 3) 包装类和基本数据类型之间的转换 包装类------ 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 类型,那么 Hibernate 就可以根据其值是否为 null 而判断一个对象是否是临 时的,如果将 OID 定义为了 int 类型,还需要在 hbm 映射文件中 设置其 unsaved-value 属性为 0。 另外,Integer 提供了多个与整数相关的操作方法,例如,将 一个字符串转换成整数,Integer 中还定义了表示整数的最大值 和最小值的常量。 String 类为什么是 final 的 1. 为了效率。若允许被继承,则其高度的 被使用率可能会降低程 序的性能。 2. 为了安全。JDK 中提供的好多核心类比如 String,这类的类的 内部好多方法的实现都不是 java 编程语言本身编写的,好多方法都 是调用的操作系统本地的 API,这就是著名的“本地方法调用”,也 只有这样才能做事,这种类是非常底层的, 和操作系统交流频繁的,
那么如果这种类可以被继承的话,如果我们再把它的方法重写了,往 操作系统内部写入一段具有恶意攻击性质的代码什么的, 这不就成 了核心病毒了么? 3. 不希望别人改,这个类就像一个工具一样,类的提供者给我们 提供了, 就希望我们直接用就完了,不想让我们随便能改,其实说 白了还是安全性, 如果随便能改了,那么 java 编写的程序肯定就很 不稳定,你可以保证自己不乱改, 但是将来一个项目好多人来做, 管不了别人,再说有时候万一疏忽了呢?他也不是估计的, 所以这 个安全性是很重要的,java 和 C++相比,优点之一就包括这一点; String、StringBuffer、StringBuilder 区别与联系 1. String 类是不可变类,即一旦一个 String 对象被创建后,包 含在这个对象中的字符序列是不可改变的,直至这个对象销毁。 2. StringBuffer 类则代表一个字符序列可变的字符串,可以通过 append、insert、reverse、setChartAt、setLength 等方法改变其 内容。一旦生成了最终的字符串,调用 toString 方法将其转变为 String 3. JDK1.5 新增了一个 StringBuilder 类,与 StringBuffer 相似, 构造方法和方法基本相同。不同是 StringBuffer 是线程安全的,而 StringBuilder 是线程不安全的,所以性能略高。通常情况下,创建 一个内容可变的字符串,应该优先考虑使用 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 的区别和联系 实现原理相同,功能相同,都是长度可变的数组结构,很多情况 下可以互用 两者的主要区别如下 1. Vector 是早期 JDK 接口,ArrayList 是替代 Vector 的新接口 2. Vector 线程安全,ArrayList 重速度轻安全,线程非安全 3. 长度需增长时,Vector 默认增长一倍,ArrayList 增长 50%
ArrayList 和 LinkedList 的区别和联系 两者都实现了 List 接口,都具有 List 中元素有序、不唯一的特 点。ArrayList 实现了长度可变的数组,在内存中分配连续空间。遍 历元素和随机访问元素的效率比较高; LinkedList 采用链表存储方式。插入、删除元素时效率比较高 HashMap 和 Hashtable 的区别和联系 实现原理相同,功能相同,底层都是哈希表结构,查询速度快, 在很多情况下可以互用 两者的主要区别如下 1. Hashtable 是早期 JDK 提供的接口,HashMap 是新版 JDK 提供的 接口 2. Hashtable 继承 Dictionary 类,HashMap 实现 Map 接口 3. Hashtable 线程安全,HashMap 线程非安全 4. Hashtable 不允许 null 值,HashMap 允许 null 值 HashSet 的使用和原理(hashCode()和 equals())
11. 哈希表的查询速度特别快,时间复杂度为 O(1)。 2. HashMap、Hashtable、HashSet 这些集合采用的是哈希表结构, 需要用到 hashCode 哈希码,hashCode 是一个整数值。 3. 系统类已经覆盖了 hashCode 方法 自定义类如果要放入 hash 类集合,必须重写 hashcode。如果不重写,调用的是 Object 的 hashcode,而 Object 的 hashCode 实际上是地址。 4. 向哈希表中添加数据的原理:当向集合 Set 中增加对象时,首 先集合计算要增加对象的 hashCode 码,根据该值来得到一个位置用 来存放当前对象,如在该位置没有一个对象存在的话,那么集合 Set 认为该对象在集合中不存在,直接增加进去。如果在该位置有一个对 象存在的话,接着将准备增加到集合中的对象与该位置上的对象进行 equals 方法比较,如果该 equals 方法返回 false,那么集合认为集合 中不存在该对象,在进行一次散列,将该对象放到散列后计算出的新 地址里。如果 equals 方法返回 true,那么集合认为集合中已经存在 该对象了,不会再将该对象增加到集合中了。 5. 在哈希表中判断两个元素是否重复要使用到 hashCode()和 equals()。hashCode 决定数据在表中的存储位置,而 equals 判断是 否存在相同数据。 6. 6) Y=K(X) :K 是函数,X 是哈希码,Y 是地址
TreeSet 的原理和使用(Comparable 和 comparator) 1. TreeSet 中的元素不允许重复,但是有序 2. TreeSet 采用树结构存储数据,存入元素时需要和树中元素进 行对比,需要指定比较策略。可以通过 Comparable 和 Comparator 来 指定比较策略。 3. 实现了 Comparable 的系统类可以顺利存入 TreeSet。自定义类 可以实现 Comparable 接口来指定比较策略。 4. 可创建 Comparator 接口实现类来指定比较策略,并通过 TreeSet 构造方法参数传入。这种方式尤其对系统类非常适用。 集合和数组的比较(为什么引入集合) 数组不是面向对象的,存在明显的缺陷,集合完全弥补了数组的 一些缺点,比数组更灵活更实用,可大大提高软件的开发效率而 且不同的集合框架类可适用于不同场合。具体如下: 1. 数组的效率高于集合类. 2. 数组能存放基本数据类型和对象,而集合类中只能放对象。 3. 数组容量固定且无法动态改变,集合类容量动态改变。 4. 数组无法判断其中实际存有多少元素,length 只告诉了 array 的容量。
12. 集合有多种实现方式和不同的适用场合,而不像数组仅采用顺 序表方式。 6. 集合以类的形式存在,具有封装、继承、多态等类的特性,通 过简单的方法和属性调用即可实现各种复杂操作,大大提高软件的开 发效率。 Collection 和 Collections 的区别 Collection 是 Java 提供的集合接口,存储一组不唯一,无序的 对象。它有两个子接口 List 和 Set。 Java 中还有一个 Collections 类,专门用来操作集合类 ,它提 供一系列静态方法实现对各种集合的搜索、排序、线程安全化等 操作。
第九章 IO 流 输入流和输出流联系和区别,节点流和处理流联系和区别 首先,你要明白什么是“流”。直观地讲,流就像管道一样, 在程序和文件之间,输入输出的方向是针对程序而言,向程序中 读入东西,就是输入流,从程序中向外读东西,就是输出流。输 入流是得到数据,输出流是输出数据。 而节点流,处理流是流的另一种划分,按照功能不同进行的 划分。节点流,可以从或向一个特定的地方(节点)读写数据。处 理流是对一个已存在的流的连接和封装,通过所封装的流的功能 调用实现数据读写。如 BufferedReader。处理流的构造方法总是 要带一个其他的流对象做参数。一个流对象经过其他流的多次包 装,称为流的链接。 字符流字节流联系区别;什么时候使用字节流和字符流? 字符流和字节流是流的一种划分,按处理照流的数据单位进行 的划分。两类都分为输入和输出操作。在字节流中输出数据主要 是使用 OutputStream 完成,输入使的是 InputStream,在字符流 中输出主要是使用 Writer 类完成,输入流主要使用 Reader 类完 成。这四个都是抽象类。字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符、字符数组或字符串,而字节流处理单元为
1 个字节,操作字节和字节数组。字节流是最基本的,所有的 InputStrem 和 OutputStream 的子类都是,主要用在处理二进制数 据,它是按字节来处理的 但实际中很多的数据是文本,又提出了 字符流的概念,它是按虚拟机的编码来处理,也就是要进行字符 集 的 转 化 这 两 个 之 间 通 过 InputStreamReader,OutputStreamWriter 来关联,实际上是通过 byte[]和 String 来关联的。 列举常用字节输入流和输出流并说明其特点 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 读取。不建议对这两个对象尝试使用单个线 程,因为这样可能会造成该线程死锁。如果某个线程正从连接的 管道输入流中读取数据字节,但该线程不再处于活动状态,则该 管道被视为处于毁坏状态。 缓冲流的优点和原理 不带缓冲的流的工作原理:它读取到一个字节/字符,就向用 户指定的路径写出去,读一个写一个,所以就慢了。带缓冲的流 的工作原理:读取到一个字节/字符,先不输出,等凑足了缓冲的 最大容量后一次性写出去,从而提高了工作效率 优点:减少对硬盘的读取次数,降低对硬盘的损耗。
序列化的定义、实现和注意事项 想把一个对象写在硬盘上或者网络上,对其进行序列化,把他序 列化成为一个字节流。 实现和注意事项: 1. 实现接口 Serializable Serializable 接口中没有任何的方法, 实现该接口的类不需要实现额外的方法。 2. 如果对象中的某个属性是对象类型,必须也实现 Serializable 接口才可以 3. 序列化对静态变量无效 4. 如果不希望某个属性参与序列化,不是将其 static,而是 transient 5. 串行化保存的只是变量的值,对于变量的任何修饰符,都不能 保存 6. 序列化版本不兼容 使用 IO 流完成文件夹复制(结合递归) import java.io.*; /** * CopyDocJob 定义了实际执行的任务,即 * 从源目录拷贝文件到目标目录
/public class CopyDir2 { public static void main(String[] args) { try { copyDirectiory(“d:/301sxt”,“d:/301sxt2”); } catch (IOException e) { e.printStackTrace(); }}/* * 复制单个文件 * @param sourceFile 源文件 * @param targetFile 目标文件 * @throws IOException /private static void copyFile(File sourceFile, File targetFile) throws IOException { BufferedInputStream inBuff = null; BufferedOutputStream outBuff = null; try { // 新建文件输入流
inBuff = new BufferedInputStream(new FileInputStream(sourceFile)); // 新建文件输出流 outBuff = new BufferedOutputStream(new FileOutputStream(targetFile)); // 缓冲数组 byte[] b = new byte[1024 * 5]; int len; while ((len = inBuff.read(b)) != -1) { outBuff.write(b, 0, len); }// 刷新此缓冲的输出流 outBuff.flush(); } finally { // 关闭流 if (inBuff != null) inBuff.close(); if (outBuff != null) outBuff.close(); }}
/
* * 复制目录 * @param sourceDir 源目录 * @param targetDir 目标目录 * @throws IOException /private static void copyDirectiory(String sourceDir, String targetDir) throws IOException { // 检查源目录 File fSourceDir = new File(sourceDir); if(!fSourceDir.exists() || !fSourceDir.isDirectory()){ return; }//检查目标目录,如不存在则创建 File fTargetDir = new File(targetDir); if(!fTargetDir.exists()){ fTargetDir.mkdirs(); }// 遍历源目录下的文件或目录 File[] file = fSourceDir.listFiles(); for (int i = 0; i < file.length; i++) { if (file[i].isFile()) {
// 源文件 File sourceFile = file[i]; // 目标文件 File targetFile = new File(fTargetDir, file[i].getName()); copyFile(sourceFile, targetFile); }//递归复制子目录 if (file[i].isDirectory()) { // 准备复制的源文件夹 String subSourceDir = sourceDir + File.separator + file[i].getName(); // 准备复制的目标文件夹 String subTargetDir = targetDir + File.separator + file[i].getName(); // 复制子目录 copyDirectiory(subSourceDir, subTargetDir); }}}}
第十章 多线程 进程和线程有什么联系和区别?
1.定义: 1. 进程是具有一定独立功能的程序关于某个数据集合上的一次运 行活动,是系统进行资源分配和调度的一个独立单位。 2. 线程是进程的一个实体,是 CPU 调度和分派的基本单位,他是 比进程更小的能独立运行的基本单位,线程自己基本上不拥有系 统资源,只拥有一点在运行中必不可少的资源(如程序计数器, 一组寄存器和栈),一个线程可以创建和撤销另一个线程; 2.进程和线程的关系: (1)一个线程只能属于一个进程,而一个进程可以有多个线程, 但至少有一个线程。 (2)资源分配给进程,同一进程的所有线程共享该进程的所有资 源。(3)线程在执行过程中,需要协作同步。不同进程的线程间要利 用消息通信的办法实现同步。 (4)处理机分给线程,即真正在处理机上运行的是线程。 (5)线程是指进程内的一个执行单元,也是进程内的可调度实体。 3.线程与进程的区别: (1)调度:线程作为调度和分配的基本单位,进程作为拥有资源 的基本单位。 (2)并发性:不仅进程之间可以并发执行,同一个进程的多个线 程之间也可以并发执行。
(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系 统资源,但可以访问隶属于进程的资源。 (4)系统开销:在创建或撤销进程的时候,由于系统都要为之分 配和回收资源,导致系统的明显大于创建或撤销线程时的开销。 但进程有独立的地址空间,进程崩溃后,在保护模式下不会对其 他的进程产生影响,而线程只是一个进程中的不同的执行路径。 线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间, 一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线 程的程序健壮,但是在进程切换时,耗费的资源较大,效率要差 些 创建线程的两种方式分别是什么,优缺点是什么? 方式 1:继承 Java.lang.Thread 类,并覆盖 run() 方法。优势: 编写简单;劣势:无法继承其它父类 public class ThreadDemo1 { public static void main(String args[]) { MyThread1 t = new MyThread1(); t.start(); while (true) { System.out.println(“兔子领先了,别骄傲”); }}
}class MyThread1 extends Thread { public void run() { while (true) { System.out.println(“乌龟领先了,加油”); }}}方式 2:实现 Java.lang.Runnable 接口,并实现 run()方法。优 势:可继承其它类,多线程可共享同一个 Thread 对象;劣势:编 程 方 式 稍 微 复 杂 , 如 需 访 问 当 前 线 程 , 需 调 用 Thread.currentThread()方法 public class ThreadDemo2 { public static void main(String args[]) { MyThread2 mt = new MyThread2(); Thread t = new Thread(mt); t.start(); while (true) { System.out.println(“兔子领先了,加油”); }}}
class MyThread2 implements Runnable { public void run() { while (true) { System.out.println(“乌龟超过了,再接再厉”); }}} 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 地址分类 IPV4:32 位地址,以点分十进制表示,如 192.168.0.1 IPV6:128 位(16 个字节)写成 8 个 16 位的无符号整数,每个整数用 四 个 十 六 进 制 位 表 示 , 数 之 间 用 冒 号 ( : ) 分 开 , 如 : 3ffe:3201:1401:1280:c8ff:fe4d:db39:1984 特殊的 IP 地址 127.0.0.1 本机地址 192.168.0.0–192.168.255.255 私有地址,属于非注册地址,专门为组 织机构内部使用。 端口:port IP 地址用来标志一台计算机,但是一台计算机上可能提供多种应用程 序,使用端口来区分这些应用程序。
端口是虚拟的概念,并不是说在主机上真的有若干个端口。通过端口, 可以在一个主机上运行多个网络应用程序。 端口范围 0—65535,16 位整数 端口分类 公认端口 0—1023 比如 80 端口分配给 WWW,21 端口分配给 FTP,22 端 口分配给 SSH,23 端口分配给 telnet,25 端口分配给 smtp 注册端口 1024—49151 分配给用户进程或应用程序 动态/私有端口 49152–65535 理解 IP 和端口的关系 IP 地址好比每个人的地址(门牌号),端口好比是房间号。必须同时指 定 IP 地址和端口号才能够正确的发送数据 IP 地址好比为电话号码,而端口号就好比为分机号。 介绍 OSI 七层模型和 TCP/IP 模型
13. OSI(Open System Interconnection),开放式系统互联参考模 型 。是一个逻辑上的定义,一个规范,它把网络协议从逻辑上分 为了 7 层。每一层都有相关、相对应的物理设备,比如常规的路 由器是三层交换设备,常规的交换机是二层交换设备。OSI 七层 模型是一种框架性的设计方法,建立七层模型的主要目的是为解 决异种网络互连时所遇到的兼容性问题,其最主要的功能就是帮 助不同类型的主机实现数据传输。它的最大优点是将服务、接口 和协议这三个概念明确地区分开来,通过七个层次化的结构模型 使不同的系统不同的网络之间实现可靠的通讯。 2. TCP/IP 协议是 Internet 最基本的协议、Internet 国际互联网 络的基础,主要由网络层的 IP 协议和传输层的 TCP 协议组成。 TCP/IP 定义了电子设备如何连入因特网,以及数据如何在它们之 间传输的标准。协议采用了 4 层的层级结构,每一层都呼叫它的 下一层所提供的协议来完成自己的需求。 3. ISO 制定的 OSI 参考模型的过于庞大、复杂招致了许多批评。 伴随着互联网的流行,其本身所采用的 TCP/IP 协议栈获得了更为
广泛的应用和认可。在 TCP/IP 参考模型中,去掉了 OSI 参考模型 中的会话层和表示层(这两层的功能被合并到应用层实现)。同 时将 OSI 参考模型中的数据链路层和物理层合并为主机到网络 层。 TCP 协议和 UDP 协议的比较 TCP 和 UDP 是 TCP/IP 协议栈中传输层的两个协议,它们使用 IP 路由功能把数据包发送到目的地,从而为应用程序及应用层协议 (包括:HTTP、SMTP、SNMP、FTP 和 Telnet)提供网络服务。 TCP 的 server 和 client 之间通信就好比两个人打电话,需要互 相知道对方的电话号码,然后开始对话。所以在两者的连接过程 中间需要指定端口和地址。
UDP 的 server 和 client 之间的通信就像两个人互相发信。我只 需要知道对方的地址,然后就发信过去。对方是否收到我不知道, 也不需要专门对口令似的来建立连接。具体区别如下: 1. TCP 是面向连接的传输。UDP 是无连接的传输 2. TCP 有流量控制、拥塞控制,检验数据数据按序到达,而 UDP 则相反。 3. TCP 的路由选择只发生在建立连接的时候,而 UDP 的每个报文 都要进行路由选择 4. TCP 是可靠性传输,他的可靠性是由超时重发机制实现的,而 UDP 则是不可靠传输 5. UDP 因为少了很多控制信息,所以传输速度比 TCP 速度快 6. TCP 适合用于传输大量数据,UDP 适合用于传输小量数据 什么是 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 方法,关闭。
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 包中 1. Class 类:代表一个类 2. Field 类:代表类的成员变量(属性) 3. Method 类:代表类的成员方法 4. Constructor 类:代表类的构造方法 5. Array 类:提供了动态创建数组,以及访问数组的元素的静态 方法 Class 类的作用?生成 Class 对象的方法有哪些? Class 类是 Java 反射机制的起源和入口,用于获取与类相关的各 种信息,提供了获取类信息的相关方法。Class 类继承自 Object 类Class 类是所有类的共同的图纸。每个类有自己的对象,好比图 纸和实物的关系;每个类也可看做是一个对象,有共同的图纸 Class,存放类的结构信息,能够通过相应方法取出相应信息:类 的名字、属性、方法、构造方法、父类和接口
方 法 示 例 对象名.getClass() String str=“bdqn”; Class clazz = str.getClass(); 对象名.getSuperClas s() 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.OracleD river”); 类名.class Class c1 = String.class; Class c2 = Student.class; Class c2 = int.class 包装类.TYPE Class c1 = Integer.TYPE; Class c2 = Boolean.TYPE; 反射的使用场合和作用、及其优缺点 1. 反射的使用场合和作用
使用场合:在编译时根本无法知道该对象或类可能属于哪些类, 程序只依靠运行时信息来发现该对象和类的真实信息。 主要作用:通过反射可以使程序代码访问装载到 JVM 中的类的内 部信息 1. 获取已装载类的属性信息 2. 获取已装载类的方法 3. 获取已装载类的构造方法信息 1. 反射的优点 反射提高了 Java 程序的灵活性和扩展性,降低耦合性,提高自适 应能力。它允许程序创建和控制任何类的对象,无需提前硬编码 目标类;反射是其它一些常用语言,如 C、C++、Fortran 或者 Pascal 等都不具备的 Java 反射技术应用领域很广,如软件测试、 EJB、JavaBean 等; 许多流行的开源框架例如 Struts、Hibernate、Spring 在实现过 程中都采用了该技术 1. .反射的缺点 性能问题:使用反射基本上是一种解释操作,用于字段和方法接 入时要远慢于直接代码。因此 Java 反射机制主要应用在对灵活性 和扩展性要求很高的系统框架上,普通程序不建议使用。
使用反射会模糊程序内部逻辑:程序人员希望在源代码中看到 程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。 反射代码比相应的直接代码更复杂。
第十三章 设计模式入门 什么是设计模式,设计模式的作用 设计模式是一套被反复使用的、多数人知晓、经过分类编目 的优秀代码设计经验的总结。特定环境下特定问题的处理方法。 重用设计和代码 重用设计比重用代码更有意义,自动带来代码 重用 提高扩展性 大量使用面向接口编程,预留扩展插槽,新的功能或特性 很容易加入到系统中来 提高灵活性 通过组合提高灵活性,可允许代码修改平稳发生,对一处 修改不会波及到其他模块 提高开发效率 正确使用设计模式,可以节省大量的时间 面向对象设计原则有哪些 面向对象设计原则是面向对象设计的基石,面向对象设计质量的 依据和保障,设计模式是面向对象设计原则的经典应用 1. 单一职责原则 SRP 2. 开闭原则 OCP 3. 里氏替代原则 LSP
14. 依赖注入原则 DIP 5. 接口分离原则 ISP 6. 迪米特原则 LOD 7. 组合/聚合复用原则 CARP 开闭原则具有理想主义的色彩,它是面向对象设计的终极目 标。其他设计原则都可以看作是开闭原则的实现手段或方法 23 种经典设计模式都有哪些,如何分类 简单工厂模式的示例 public class SimpleFactory {
public static Product createProduct(String pname){ Product product=null; if(“p1”.equals(pname)){ product = new Product1(); }else if(“p2”.equals(pname)){ product = new Product2(); }else if(“pn”.equals(pname)){ product = new ProductN(); }return product; }}基本原理:由一个工厂类根据传入的参数(一般是字符串参数), 动态决定应该创建哪一个产品子类(这些产品子类继承自同一个 父类或接口)的实例,并以父类形式返回 优点:客户端不负责对象的创建,而是由专门的工厂类完成;客 户端只负责对象的调用,实现了创建和调用的分离,降低了客户 端代码的难度; 缺点:如果增加和减少产品子类,需要修改简单工厂类,违背 了开闭原则;如果产品子类过多,会导致工厂类非常的庞大,违 反了高内聚原则,不利于后期维护
简单的单例模式的示例 /
* * 饿汉式的单例模式 * 在类加载的时候创建单例实例,而不是等到第一次请求实例的 时候的时候创建 * 1、私有 的无参数构造方法 Singleton(),避免外部创建实例 * 2、私有静态属性 instance * 3、公有静态方法 getInstance() /public class Singleton { private static Singleton instance = new Singleton(); private Singleton(){ } public static Singleton getInstance(){ return instance; }}/* * 懒汉式的单例模式 在类加载的时候不创建单例实例,只有在第一次请求实例的时候 的时候创建 /public class Singleton {
private static Singleton instance; private Singleton(){ } /
* 多线程情况的单例模式,避免创建多个对象 */public static Singleton getInstance(){ if(instance null){//避免每次加锁,只有第一次没有创建对 象时才加锁 synchronized(Singleton.class){//加锁,只允许一个线程进入 if(instancenull){ //只创建一次对象 instance = new Singleton(); }}}return instance; }} 一个设计模式进行介绍 分析:建议挑选有一定技术难度,并且在实际开发中应用较多的 设计模式。可以挑选装饰模式和动态代理模式。此处挑选动态代 理设计模式。
讲解思路:生活案例引入、技术讲解、优缺点分析、典型应用。 案例1、生活案例引入:送生日蛋糕: MM 们要过生日了,怎么也得表示下吧。最起码先送个蛋糕。蛋糕多 种多样了。巧克力,冰淇淋,奶油等等。这都是基本的了,再加点额 外的装饰,如蛋糕里放点花、放贺卡、放点干果吃着更香等等。 分析: 方案 1:如果采用继承会造成大量的蛋糕子类 方案 2、蛋糕作为主体,花,贺卡,果仁等是装饰者,需要时加到蛋 糕上。要啥我就加啥。 2、技术讲解 装饰模式(别名 Wrapper)是在不必改变原类文件和使用继承的情况 下,动态的扩展一个对象的功能。它通过创建一个包装对象,也就是 装饰来包裹真实对象,提供了比继承更具弹性的代替方案。 装饰模式一般涉及到的角色 抽象构建角色(Component):给出一个抽象的接口,以规范准备接受附 加责任的对象。 具体的构建角色(ConcreteComponent):定义一个将要接受附加责任 的类。
抽象的装饰角色 (Decorator):持有一个抽象构建(Component)角色 的引用,并定义一个与抽象构件一致的接口。 具体的装饰角色(ConcreteDecorator):负责给构建对象“贴上”附加 的责任。 3、优缺点分析 优点Decorator 模式与继承关系的目的都是要扩展对象的功能,但是 Decorato 更多的灵活性。 把类中的装饰功能从类中搬移出去,这样可以简化原有的类。有效地 把类的核心功能和装饰功能区分开了。 通过使用不同的具体装饰类以及这些装饰类的排列组合,可创造出很 多不同行为的组合。
缺点这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得 很复杂。 符合的设计原则: 多用组合,少用继承。利用继承设计子类的行为是在编译时静态决定 的,且所有的子类都会继承到相同的行为。如能够利用组合扩展对象 的行为,就可在运行时动态进行扩展。 类应设计的对扩展开放,对修改关闭。 4、典型应用 java IO 中需要完成对不同输入输出源的操作,如果单纯的使 用继承这一方式,无疑需要很多的类。比如说,我们操作文件需 要一个类,实现文件的字节读取需要一个类,实现文件的字符读 取又需要一个类…一次类推每个特定的操作都需要一个特定的 类。这无疑会导致大量的 IO 继承类的出现。显然对于编程是很不 利的。而是用装饰模式则可以很好的解决这一问题,在装饰模式中: 节点流(如 FileInputStream)直接与输入源交互,之后通过过
滤流(FilterInputStream)进行装饰,这样获得的 io 对象便具 有某几个的功能,很好的拓展了 IO 的功能。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值