Java是一门面向对象的编程语言,而C语言是一个面向过程的语言,Java语言具有功能强大和简单易用两个特征,Java是静态面向对象编程语言代表,Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点。
Java是强类型的语言,必须先定义,后使用;指定类型的变量只能接受类型与之匹配的值。
Java入门及环境搭建
Java分为三个体系:JavaSE、JavaEE、JavaME
Java还是一个平台,Java平台有虚拟机和Java应用编程接口构成。
Java是一种面向对象的语言,它对对象中的类、对象、继承、封装、多态、接口、包装均有很好的支持。
为了简单,Java只支持类之间的单继承,但是可以使用接口来进行多继承。
Package com.zpark;
//定义类
Public class HelloWord {
//定义程序入口(主函数)
Public static void main(String arg[]{
System.out.println(“Hello word java”) ;
}
}
java源程序可以使用文本编译器编写:记事本
保存文件后修改文件后缀为.java
打开cmd,输入文件名.java继续编译
运行编译的.class文件,输入Java+文件名
Java命名规则
编程规范是对编程的一种约定,主要作用是增强代码的可读性和可维护性,便于代码重用。
Java类的命名规则:
类的首字母大写,如果由多个单词组成,则每个单词首字母大写(大驼峰命名法) 如:HelloWorld Demo01
首个单词的首字母小写,后面的单词首字母大写 (小驼峰命名法) 如:helloWorld dayMyYou
常量:是形式化的表现形式
常量值:是常量的具体和直观的表现形式;
常量值的分类:
整型常量值:十进制(12) 、 八进制(0125) 、十六进制(0x345)
实型常量值:十进制(12.56) 、 科学计数法(1.75e5 或 32E8)
布尔型常量值:只有两个值 true 和 false;
常量命名规则:
1.常量名全用大写;(如:PI , PRICE)
2.当常量名由多个单词组成时采用下划线分割。(如:HTTP_CODE, HTTP_STATUS_CODE)
标识符:由数字、字母、下划线(-)及$组成。如:d12abc;
数字不能作为标识符的开始。如:12as;
用户自定义标识符:是由用户按标识符的规则生成的非保留字的标识符。如:abc , name 就是一个标识符, myint , MString , intString(也是标识符)。
基本数据类型(八种)
byte 字节型(-128–127)、
short 短整型 (-32768–32767) 、int 整型 、 long 长整型 、
float 单精度浮点型 (6–7个有效位)
double 双精度浮点型 (15个有效位) 、 char 字符型 、
Boolean 布尔型
变量的作用域:
全局变量:定义在方法(函数)和代码块之外的变量;
局部变量:定义在方法或者代码块之间的变量。
整形常量默认在内存中占32位
长整形类型在内存占64位
常量一但被赋值则不可再被修改
引用数据类型:包括 数组 、类 和 接口
除了基本数据类型,其他全部是引用数据类型 如:String 、数组 等。
null类型:null可以转换任何引用类型
引用数据类型
引用数据类型建立在基本数据类型的基础上,包括数组、类和接口。引用数据类型是由用户自定义,用来限制其他数据的类型。
数据类型的转换可以分为隐式转换(自动类型转换)和显式转换(强制类型转换)两种。
如果以下 2 个条件都满足,那么将一种类型的数据赋给另外一种类型变量的时,将执行自动类型转换(automatic type conversion)。 1、两种数据类型彼此兼容 2、目标类型的取值范围大于源数据类型(低级类型数据转换成高级类型数据)
byte 1字节;short 2字节; char 2字节; int 4字节;
long 8字节; float 4字节; double 8字节;
类型转换:1、小类型数据转大类型数据(自动转换)
byte–>short–>int–>long–>float–>double
char
2、大类型数据转小类型数据(强制转换)
强制转换方法:{小类型 变量名 = (需要转换的类型)大类型}
注意:大类型转换为小类型数据可能造成数据进度丢失和溢出。
Double–>float–>long–>int–>short–>byte
Char
short和char互相强转
引用数据类型和基本数据类型不能转换,两种数据类型彼此兼容,否则不能互相转换,
引用数据类型中,除非是继承关系,否则是平等关系无大小之分
数据之间进行运算,结果会往大类型转换
Java算术运算符
一元运算符:
-:取反符合;
++:自加一(先取值再加一);
–:自减一(先取值再减一);
-a 是对 a 取反运算,a++ 或 a-- 是在表达式运算完后,再给 a 加一或减一。而 ++a 或 --a 是先给 a 加一或减一,然后再进行表达式运算。
二元运算符:
+:加,求a加b的和,还可用于String的类型,进行字符串连接操作
-:减,求 a 减 b 的差
*:乘,求 a 乘以 b 的积
/:除,求 a 除以 b 的商
%:取余,求 a 除以 b 的余数
赋值运算符:
+=:加赋值
-=:减赋值
*=:乘赋值
=:出赋值
%=:取余赋值
Java逻辑运算符:
&&:短路与,ab 全为 true 时,计算结果为 true,否则为 false
||:短路或,ab 全为 false 时,计算结果为 false,否则为 true
!:逻辑非,a 为 true 时,值为 false,a 为 false 时,值为 true
|:逻辑或,ab 全为 false 时,计算结果为 false,否则为 true
&:逻辑与,ab 全为 true 时,计算结果为 true,否则为 false
· && 与 & 区别:如果 a 为 false,则不计算 b(因为不论 b 为何值,结果都为 false
· || 与 | 区别:如果 a 为 true,则不计算 b(因为不论 b 为何值,结果都为 true)
关系运算符:
: 只支持左右两边操作数是数值类型。如果前面变量的值大于后面变量的值, 则返回 true
=:只支持左右两边操作数是数值类型。如果前面变量的值大于等于后面变量的值, 则返回 true
<: 只支持左右两边操作数是数值类型。如果前面变量的值小于后面变量的值,则返回 true。
<=:只支持左右两边操作数是数值类型。如果前面变量的值小于等于后面变量的值, 则返回 true。
==:如果进行比较的两个操作数都是数值类型,无论它们的数据类型是否相同,只要它们的值相等,也都将返回 true。 如果两个操作数都是引用类型,只有当两个引用变量的类型具有父子关系时才可以比较,只要两个引用指向的不是同一个对象就会返回 true。 Java 也支持两个 boolean 类型的值进行比较。
!=:如果进行比较的两个操作数都是数值类型,无论它们的数据类型是否相同,只要它们的值不相等,也都将返回 true。 如果两个操作数都是引用类型,只有当两个引用变量的类型具有父子关系时才可以比较,只要两个引用指向的不是同一个对象就会返回 true
自增和自减运算符:
i++:将 i 的值先使用再加 1 赋值给 i 变量本身
++i:将 i 的值先加 1 赋值给变量 i 本身后再使用
i–:将 i 的值先使用再减 1 赋值给变量 i 本身
–i:将 i 的值先减 1 后赋值给变量 i 本身再使用
& 按位进行与运算(AND)
| 按位进行或运算(OR)
^ 按位进行异或运算(XOR)
~ 按位进行取反运算(NOT)
位与运算符为&,其运算规则是:参与运算的数字,低位对齐,高位不足的补零,如果对应的二进制位同时为 1,那么计算结果才为 1,否则为 0。
任何数据类型与字符串做+运算,都会变为字符串
equals(obj)方法和==的区别:
==比较的是两个对象在内存中存放的地址是否为同一个
equals(obj)比较的是内容是否相同
next()与nextLine()方法的区别:
1、当遇到空格或者回车的时候next()方法停止读取
2、当遇到回车的时候nextLink()停止读取,读取整行数据
位移运算符:
» 右移位运算符
« 左移位运算符
左移位运算符为«,其运算规则是:按二进制形式把所有的数字向左移动对应的位数,高位移出(舍弃),低位的空位补零。
右位移运算符为»,其运算规则是:按二进制形式把所有的数字向右移动对应的位数,低位移出(舍弃),高位的空位补零。
运算符优先级:
1 ()、[]、{}
2 !、+、-、~、++、–
3 、/、%
4 +、-
5 «、»、>>>
6 <、<=、>、>=、instanceof
7 ==、!=
8 &
9 ^
10 |
11 &&
12 ||
13 ?:
14 =、+=、-=、=、/=、&=、|=、^=、~=、«=、»=、>>>=
直接量类型:
1)int 类型的直接量
在程序中直接给出的整型数值,可分为二进制、十进制、八进制和十六进制 4 种,其中二进制需要以 0B 或 0b 开头,八进制需要以 0 开头,十六进制需要以 0x 或 0X 开头。例如 123、012(对应十进制的 10)、0x12(对应十进制的 18)等。
2)long 类型的直接量
在整型数值后添加 l 或 L 后就变成了 long 类型的直接量。例如 3L、0x12L(对应十进制的 18L)。
3)float 类型的直接量
在一个浮点数后添加 f 或 F 就变成了 float 类型的直接量,这个浮点数可以是标准小数形式,也可以是科学计数法形式。例如 5.34F、3.14E5f。
4)double 类型的直接量
直接给出一个标准小数形式或者科学计数法形式的浮点数就是 double 类型的直接量。例如 5.34、3.14E5。
5)boolean 类型的直接量
这个类型的直接量只有 true 和 false。
6)char 类型的直接量
char 类型的直接量有三种形式,分别是用单引号括起来的字符、转义字符和 Unicode 值表示的字符。例如‘a’,‘\n’和‘\u0061’。
7)String 类型的直接量
一个用双引号括起来的字符序列就是 String 类型的直接量。
在大多数其他语言中,包括 C/C++,字符串作为字符的数组被实现。然而,在 Java 中并非如此。在 Java 中,字符串实际上是对象类型。在教程后面你将看到,因为 Java 对字符串是作为对象实现的,因此,它有广泛的字符串处理能力,而且功能既强又好用。
8)null 类型的直接量
这个类型的直接量只有一个值,即 null。
在上面的 8 种类型的直接量中,null 类型是一种特殊类型,它只有一个值:null。而且这个直接量可以赋给任何引用类型的变量,用以表示这个引用类型变量中保存的地址为空,即还未指向任何有效对象。
**Java扫描器:**用户从控制台输入数据,后台java程序接收
扫描器:(Scanner)
编译期:是指把源码交给编译器编译成计算机可以执行的文件的过程。在Java中也就是把Java代码编成class文件的过程.编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误。
运行期:是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来,在Java中把磁盘中的代码放到内存中就是类加载过程,类加载是运行期的开始部分。
for循环嵌套:嵌套循环既可以是for循环嵌套while循环,也可以是while循环嵌套 do-while循环…即各种类型的循环都可以作为外层循环,也可以作为内层循环。 +
当程序遇到嵌套循环时,如果外层循环的循环条件允许,则开始执行外层循环的循环体,而内层循环将被外层循环的循环体来执行——只足内层循环需要反复执行自己的循环体而已。
体。外循坏的循环亲件,决定是否再次开始执行外层循环的循环
根据上面分析,假设外层循环的循环次数为n次,内层循环的循环次数为m次,那么内层循环的循环体实际上需要执行n×m次。嵌套循环的执行流程如下图所示。
(注意,如果写的程序循环嵌套超过三层,这个程序百分百有问题)
while和do-while的比较
while 循环和do-while 循环的相同处是:都是循环结构,使用 while(循环条件)表示循环条件,使用大括号将循环操作括起来。
while 循环和do-while 循环的不同处如下:
号。这后面,而且前面多了do关键字,后面多了一个分
执行次序不同:while 循环先判断,再执行。do-while循环先执行,再判断。
一开始循环条件就不满足的情况下,while 循环一次都不会执行,do-while 循环则不管什么情况下都至少执行一次。
for循环
for语句是应用最广泛、功能最强的一种循环语句。大部分情况下,for循环可以代替while循环、do while 循环。
for语句是一种在程序执行前就要先判断条件表达式是否为真的循坏语句。假如条件表达式的结果为假,那么它的循环语句根本不会
执行。for语句通常使用在知道循环次数的循坏中。for语句语法格式如下所示。
for(表达式1;表达式2;表达式3){
语句块;
}
嵌套 switch 语句
可以将一个switch 语句作为一个外部switch语句的语句序列的一部分,这称为嵌套switch语句。因为一个switch语句定义了自己
的块,外部 switch语句和内部 switch语句的case 常量不会产生冲突。
if语句和 switch语句的区别
if和 switch语句都表示条件语句。可以从使用效率和实用性两方面加以区分。
1.从使用效率上区分
从使用效率上区分,在对同一个变量的不同值作条件判断时,既可以使用switch语句,也可以使用if语句。使用switch语句的效率更高一些,尤其是判断的分支越多,越明显。 +
2.从实用性上区分
从语句的实用性角度区分,switch语句不如if条件语句,if语句是应用最广泛和最实用的语句。
3.何时使用if语句和switch语句
在程序开发的过程中,何时使用if语句和switch语句,需要根据实际情况而定,应尽量做到物尽其用。不能因为switch语句的效率高就一直使用,也不能因为if语句常用就不用switch语句。需要根据实际情况,具体问题具体分析,使用最适合的条件语句。
一般情况下,对于判断条件较少的,可以使用if条件语句,但是在实现一些多条件的判断中,最好使用switch语句。
while和do while 循环
所有流行的编程语言中都有循环语句。Java中采用的循环语句与C语言中的循环语句相似,主要有while. do-while和for。另外java 5 之后推出 for-each 街环语句,for-each 循环是for循环的变形,它是专门为集合遍历而设计的。for-each并不是一个关键字。
循环语句可以在满足循环条件的情况下,反复执行某一段代码,这段被重复执行的代码被称为循环体。当反复执行这个循环体时,需要在合适的时候把循环条件改为假,从而结束循环,否则循环将一直执行下去,形成死循环。
循环语句可能包含如下4个部分。
1.初始化语句(init statement):一条或多条语句,这些语句用于完成一些初始化工作,初始化语句在循环开始之前执行。
2.循环条件(test_expression):这是一个boolean表达式,这个表达式能决定是否执行循环体。
3.循环体(body_statement):这个部分是循环的主体,如果循环条件允许,这个代码块将被重复执行。如果这个代码块只有一行语句,则这个代码块的花括号是可以省略的。
4.迭代语句(iteration_statement):这个部分在一次循环体执行结束后,对循环条件求值之前执行,通常用于控制循环条件中的变量,使得循环在合适的时候结束。
while 语句
while语句是Java最基本的循环语句,是一种先判断的循环结构,可以在一定条件下重复执行一段代码。该语句需要判断一个测试条牛,如果该条件为真,则执行循环语句(循环语句可以是一条或多条),否则跳出循环。
命令行传递参数
通过命令行给main方法传递参数:
java完整的类名(包名.类名).java参数1 参数2 参数3.……
例如
javacom.zpark.grammar.day01.Demo01.javaHello World Hi
可变参数
Java从JDK 1.5开始支持传递同类型的可变参数给一个方法。在方法声明中,通过给知道参数类型后面加一个省略号,从而达到传递可变参数的效果。一个方法中只能指定一个可变参数,并且它必须位于参数列表的最后一位,其余的普通参数都必须在其之前声明,例如:
public static void max(int a,string str,double…num){
方法体
}
方法的重载
重载就是在一个类中,有相同的函数名称,但参数列表不相同的方法。
方法重载的规则:
方法名字必须相同,参数列表必须不同(参数个数不同、类型不同、参数排列顺序不同等)
方法的返回值可以相同,也可以不同(方法的重载与返回值炎型无关)。
仅仅只是返回炎型不同的话,则不足以称为方法的重载。
方法签名:
方法名+参数列表
重载方法调用规则:
编译器在编译时会根据方法的签名自动绑定调用的方法
。
break 语句
某些时候需要在某种条件出现时强行终止循环,而不是等到循环条件为false时才退出循环。此时,可以使用break来完成这个功能。break 用于完全结束一个循环,跳出循环体。不管是哪种循环,一旦在循环体中遇到break,系统将完全结束该循环,开始执行循环之后的代码。
在Java中,break 语句有3种作用,分别是:在switch语句中终止一个语句序列、使用 break 语句直接强行退出循环和使用break语句实现goto的功能。
continue语句
continue语句是跳过循环休中剩余的语句而强制执行下一次循环,其作用为结束本次循坏,即跳过循环体中下面尚未执行的语句,接着进行下一次是否执行循环的判定。
continue语句类似于break语句,但它只能出现在循环体中。它与break语句的区别在于:continue并不是中断循环语句,而是中止当前迭代的循环,进入下一次的迭代。简单来讲,continue是忽略循环语句的当次循环。
注意:continue语句只能用在while语句、for语句或者 foreach语句的循坏休之中,在这之外的任何地方使用它都会引起语法错
。
递归算法:
是一种直接或者间接地调用自身的算法。在计算机编写程序中,递归算法对解决一大类问题是十分有效的,它往往使算法的描述简洁而且易于理解。
递归算法:在函数或子过程的内部,直接或者间接地调用自己的算法。
递归算法的实质:是把问题转化为规模缩小了的同类问题的子问题。然后递归调用函数(或过程)来表示问题的解。
递归算法解决问题的特点:
(1)递归就是在过程或函数里调用自身。
(2)在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。
(3)递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提信用递归算法设计程序。
(4)在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。所以一般不提倡用递归算法设计程序。
递归的两个条件:
1、可以通过递归调用来缩小问题规模,目新问题与原问题有着相同的形式。(自身调用)
2、存在一种简单情境,可以使递归在简单情境下退出。(递归出口)
递归三要素:
1、一定有一种可以退出程序的情况;
2、总是在尝试将一个问题化简到更小的规模
3、父问题与子问题不能有重叠的部分
数组以及数组的声明和创建
数组的定义:
相同数据类型元素的集合、是一种数据类型(引用类型)
数组的声明: 首先必须先声明数组变量,然后才能在程序中使用数组,数组的定义方式有如下几种:
int [] arr;或者int arr[];
数组的4个基本特点
1、数组一旦被创建,它的大小将不可以在被改变。
2、数组元素必须是相同类型的,不允许出现混合类型。
3、数组中的元素可以是任何类型,包括基本类型与引用类型。
4、数组属于引用类型,数组可以看成是一个对象,数组中的每一个元素相当于该对象的成员变量,因为数组本身就是对象,Java中对象是存放在堆中的,因此数组无论保存原始类型还是其他类型,数组对象本身都是在堆中。
构造方法的重载:
为了使用方便,可以对一个类定义多个构造方法,这些构造方法都有相同的名称,但是方法的参数列表不同,称为构造方法的重载。
java内存分析
1、堆、栈、方法区:
2、堆:new出来的对象(包括实例变量)
3、栈:局部变量(包括方法的参数)
4、方法区:.class字节码文件(包括方法、静态变量)
7、寄存器:最快的存储区,由编译器根据需求进行分配,我们在程序中无法控制;
8、2.堆:存放所有new出来的对象;
9、3.栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(对象可能在常量池里)(字符串常量对象存放在常量池中。)
10、4.静态域:存放静态成员(static定义的);
11、5.常量池:存放字符串常量和基本类型常量(public static final) 有时,在嵌入式系统中,常量本身会和其他部分分割离开(由于版权等其他原因),所以在这种情况下,可以选择将其放在ROM中
12、6.非RAM存储:硬盘等永久存储空间
三、构造器/构造方法
四、构造方法语法结构:
五、构造方法是在类中定义的方法,不同于其他的方法,构造方法的定义有如下两点规则:
六、1)构造方法的名称必须与类名完全相同。
七、2)构造方法没有返回值,连void关键字有没有
默认的构造方法:
1)任何一个类都必须含有构造方法。
2)如果源程序中没有定义构造方法,编译器在编译时会为其添加一个无参的构造方法(称为默认无参构造器).
3)当定义了构造方法之后,Java编译器不再添加默认的无参构造器。
例如:
Public class cell{
//定义属性
int row;
Int col;
}
**垃圾回收器(GC)**不定时到内存中清理垃圾,
回收过程是透明的(看不到的),不一定发现垃圾就立刻回收
调用System.gc()可以建议虚拟机尽快调度GC来回收
内存泄漏:不再使用的内存没有被及时的回收
建议:对象不再使用时及时将引用设置为null
实例变量的生命周期:
在创建对象时存储在堆中,对象被回收时一并被回收
栈:
存储正在调用的方法中的所有局部变量(包括方法的参数)
洞用方法时,会在栈中为该方法分配一块对应的栈帧,
栈帧中存储方法中的局部变量(包括参数),
方法调用结束时,栈帧被清除,局部变量一并被清除
局部变量的生命周期:
方法被调用时存储在栈中,方法结束时与栈帧一并被清除
方法区:
存储.class字节码文件(包括静态变量、方法)
方法只有一份,通过this来区分具体的调用对象
包机制
包机制的作用:
为了更好的组织类,Java提供了包机制,用于区分类的命名空间,包语句的语法格式为:package 包名1.包名2.……
一般利用公司的域名倒置作为包名。
为了能够使用包的成员,我们需要在Java程序中明确导入所使用的包,使用规则为通过import 关键字:e
import 完整的包名.类名;a +
注:
package语句必须位于Java源文件中的第一行,否则编译不通过。
继承
extends关键字
1、通过extends关键字可以实现类的继承。
2、子类可以继承父类的成员变量及成员方法,同时也可以定义自己的成员变量和成员方法。
3、Java语言不支持多重继承,一个类只能继承一个父类,但是一个父类可以有多个子类。例如:
继承中的构造方法
1、子类的构造方法中必须通过super关键字调用父类的构造方法,这样可以妥善的初始化继承自父类的成员变量。
2、如果子类的构造方法中没有调用父类的构造方法,Java编译器会自动的加入对父类的无参构造方法的调用(如果父类没有无参构造方法,则会有编译错误)。
七、封装
封装,简单的说就是该露的露,该藏的藏。我们在设计程序是要追求“高内聚,低耦合”,其中,高内聚指的是类的内部数据操作细节由自己完成,不允许外部干涉。低耦合指的是仅暴露少量的方法给外部调用(使用get/set方法)。
封装(对数据的隐藏),通常来说,应禁止直接访问应该对象中数据的实际表示,而是应该通过操作接口来访问,这种称为信息隐藏。
封装的意义:
1、对外提供可调用的,稳定的功能。
2、封装容易变化的,具体的细节,外界不可访问,这样封装的意义在于:
a.降低代码出错的可能性,便于维护。
b.当内部的实现细节改变时,只要保证对外的功能定义不变,其他的模块就不会因此而受到牵连。
封装的核心:属性私有化,行为(方法)公开化
方法的重写(Override):
1、发生在父子类中,方法名称相同,参数列表相同,方法体不同
2、重写方法被调用时,看对象的类型
3、遵循”两同两小一大"原则: 了解
3.1、两同:
1、方法名称相同
2、参数列表相同
3.2、两小:
1、派生类方法的返回值类型小于或等于超类方法的
a、void时,必须相同
b、基本数据类型时,必须相同
c、引用数据类型时,小于或等于
2、派生类方法抛出的异常小于或等于超类方法的
3.3、一大:
3.3.1、派生类方法的访问权限大于或等于超类方法的
重写与重载的区别:
1、重写(Override):
1.1发生在父子类中,方法名相同,参数列表相同,方法体不同
1.2、“运行期绑定”,看对象的类型绑定方法
2重载(Overload):
2.1发生在一个类中,方法名相同,参数列表不同,方法体不同
2.2、"编译期绑定”,看参数/引用的类型绑定方法
补充
编译期:java源文件,经过编译,生成.class字节码文件
运行期:JVM加载.class并运行.class
编译错误:只是检查语法
多态
多态指的是同一方法可以根据发送对象的不同而采用多种不同的行为方式。一个对象的实际类型是确定的,但是可以指向对象的引用的类型有很多。
多态存在的条件:
1、有继承关系
2、子类重写父类的方法
3、父类引用指向子类对象
多态的意义:
1、行为的多态(所有抽象方法都是多态的)
2、对象的多态(所有对象都是多态的)
多态的表现形式:
1、重写:根据对象的不同来表现多态 I
2、重载:根据参数的不同来表现多态
注:多态是方法的多态性,属性没有多态性。
instanceof和类型转换
instanceof是Java的一个二元操作符,类似于==,>,<等操作符。
instanceof是Java的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例(实例指的是“类”在实例化之后叫做一个"实例”。实例化:通常把用类创建对象的过程称为实例化) 返回boolean的数据类型。
向上造型:
1)超类型的引用指向派生类的对象
2)能点出来什么,看引用的类型
向下造型:(容易出现类型转换异常)
1)派生类的引用指向超类对象
2)向下造型需要进行强转
3)能点出来什么,看引用的类型
static静态块
Static块属于类的代码块,在类加载期间指向代码块,只执行一次,可以用来在软件中加载静态资源。
final修饰变量
1、final关键字修饰成员变量,表示变量不可被改变。
2、final修饰成员变量的两种方式初始化:
a.声明的同时初始化
b.构造函数中初始化
3、final关键字也可以修饰局部变量,使用之前初始化即可
final修饰方法
1.final关键字修饰的方法不可被重写。
2、使一个方法不能被重写的意义在于:防止子类在定义新方法使造成“不间意”重写。
final修饰类**
1、final关键字修饰的类不可被继承。
2、 JDK中的一些基础类库被定义为final的,例如:String、Math、Integer、Double等。
3、使一个类不能继承的意义在于:可以保护类不被继承修改,可以控制滥用继承对系统造成的危害。
static final常量
1、static final修饰的成员变量称为常量,必须声明同时初始化,不可被改变。
2、static final常量会在编译期被替换
设计模式的分
*总体来说设计模式分为三大类:
1.创建型模式,共五种:工广方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
2.结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
3.行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
其实还有两类:并发型模式和线程池模式。
设计模式的六大原则~ L
1、开闭原则(Open Close Principle)
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(LiskoySubstitution Principle)
里氏代换原则(Liskoy Substitution Principle LSP)面向对象设计的基本原则之一。里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。 From Baidu 百科
3、依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)-
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
5.迪米特法则(最少知道原则)(Demeter Principle)~
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
15、原则是尽量使用合成/聚合的方式,而不是使用继承。
1、由abstract修饰
2、2、包含抽象方法的类必须是抽象类
3、不包含抽象方法的类也可以声明为抽象类
4、3、抽象类不能被实例化
5、4、抽象类是需要被继承的派生类
6、 4.1、重写所有抽象方法 常用
7、 4.2、也声明为抽象类 般不这么做
8、5、抽象类的意义
9、 5.1、封装派生类所共有的属性和行为 代码复用
10、 5.2、给所有派生类提供统一的类型 向上造型
11、 5.3、可以包含抽象方法,为所有派生类提供统一的入口
12、派生类的具体行为不同,但入口是一致的
13、设计规则:
14、1、将派生类所共有的思性和行为,抽到超类中 抽共性
15、2、所有派生类的行为都一样,设计普通方法
16、所有派生类的行为都不一样,设计为抽象方法
接口(Interface)
接口(英文:Interface),在AVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过实现(implements)接口的方式,从而来实现接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在Java中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口与类相似点:
1)一个接口可以有多个方法。
2)接口文件保存在java结尾的文件中,文件名使用接口名。
3)接口的字节码文件保存在.class结尾的文件中。
4)接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
1)接口不能用于实例化对象。
2)接口没有构造方法。
3)接口中所有的方法必须是抽象方法。
4接口不能包含成员变量,除了static和final变量。
5)接口不是被类继承了,而是要被类实现。
6)接口支持多继承(接口不能继承类,接口只能继承接口)。
接口特性:
1)接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是public abstract,其他修饰符都会报错)。
2)接口中可以含有变量,但是接口中的变量会被隐式的指定为public static final变量(并且只能是public,用private修饰会报编译错误)。
3)接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
抽象类和接口的区别:
1.抽象类中的方法可以有方法体,就是能文现方法的具体功能,但是接口中的方法不行。
2.抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的。
3.接口中不能含有静态代码块以及静态方法(用static修饰的方法),而抽象类是可以有静态代码块和静态方法。
4.一个类只能继承一个抽象类,而一个类却可以实现多个接口。
注:JDK 1.8以后,接口里可以有静态方法和方法体了。
抽象方法:
1、由abstract修饰
2、只有方法的定义,没有具体的实现(连()都没有)
如果一个方法使用abstract来修饰,则说明该方法是抽象方法,抽象方法只有声明没有实现。需要注意的是abstract关键字只能用于普通方法,不能用于static方法或者构造方法中。
抽象方法的3个特征如下:
1)抽象方法没有方法体
2)抽象方法必须存在于抽象类中
3)子类重写父类时,必须重写父类所有的抽象方法
注意:在使用abstract关键字修饰抽象方法时不能使用private修饰,因为抽象方法必须被子类重写,而如果使用了private声明,则子类是无法重写的。
抽象类 (abstract)
抽象类描述:(在Java语言中使用abstract class来定义抽象类)
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
抽象方法:
1、由abstract修饰
2、只有方法的定义,没有具体的实现(连()都没有)
如果一个方法使用abstract来修饰,则说明该方法是抽象方法,抽象方法只有声明没有实现。需要注意的是abstract关键字只能用于普通方法,不能用于static方法或者构造方法中。
抽象方法的3个特征如下
1)抽象方法没有方法体
2)抽象方法必须存在于抽象类中
3)子类重写父类时,必须重写父类所有的抽象方法
注意:在使用abstract关键字饰抽象方法时不能使用private修饰,因为抽象方法必须被子类重写,而如果使用了private声明,则子类是无法重写的。
抽象类:
1、由abstract修饰
2、包含抽象方法的类必须是抽象类
不包含抽象方法的类也可以声明为抽象类
3、抽象类不能被实例化
4、抽象类是需要被继承的,派生类:
4.1、重写所有抽象方法-------常用
4.2、也声明为抽象类 ----- 一般不这么做
5、抽象类的意义:
5.1、封装派生类所共有的属性和行为 代码复用 +
5.2、给所有派生类提供统一的类型 向上造型
5.3、可以包含抽象方法,为所有派生类提供统的入口
派生类的具体行为不同,但入口是一致的
内部类
java内部类的几种类型:成员内部类,静态内部类,方法内部类,匿名内部类。
成员内部类:成员内部类是类内部的非静态类。成员内部类不能定义静态方法和变量(final修饰的除外)。这是因为成员内部类是非静态的,类初始化的时候先初始化静态成员,如果允许成员内部类定义静态变量,那么成员内部类的静态变量初始化顺序是有歧义的。
成员内部类的使用方法:
1、 Inner类定义在Outer类的内部,相当于Outer类的一个成员变量的位置,Inner类可以使用任意访问控制符,如public.protected、private等
2. Inner类中定义的test()方法可以直接访问Outer类中的数据,而不受访问控制符的影响,如直接访问Outer类中的私有属性a
3、定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去new一个内部类对象,即:内部类对象名=外部类对象.new内部类( );
静态内部类:
静态内部类是static修饰的内部类,这种内部类的特点是:
1、静态内部类不能直接访问外部类的非静态成员,但可以通过new 外部类().成员 的方式访问。
2、如果外部类的静态成员与内部类的成员名称相同,可通过“类名.静态成员"访问外部类的静态成员;如果外部类的静态成员与内部类的成员名称不相同,则可通过“成员名“直接调用外部类的静态成员。
3、创建静态内部类的对象时,不需要外部类的对象,可以直接创建 内部类 对象名= new 内部类();
方法内部类(局部内部类):
方法内部类就是内部类定义在外部类的方法中,方法内部类只在该方法的内部可见,即只在该方法内可以使用。
需要注意:由于方法内部类不能在外部类的方法以外的地方使用,因此方法内部类不能使用访问控制符和static修饰符。
匿名内部类:
匿名类是不能有名称的类,所以没办法引用他们。必须在创建时,作为new语句的一部分来声明他们。但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口。
这种形式的new语句声明一个新的匿名类,他对一个给定的类进行扩展,或实现一个给定的接口。他还创建那个类的一个新实例,并把他作为语句的结果而返回。要扩展的类和要实现的接口是new语句的操作数,后跟匿名类的主体。
注意匿名类的声明是在编译时进行的,实例化在运行时进行。这意味着for循环中的一个new语句会创建相同匿名类的几个实例,而不是创建几个不同匿名类的一个实例。
从技术上说,匿名类可被视为非静态的内部类,所以他们具备和方法内部声明的非静态内部类相同的权限和限制。
枚举
枚举是一个被命名的整型常数的集合,用于声明一组带标识符的常数。枚举在日常生活中很常见,例如一个人的性别只能是“男”或者“女”,一周的星期只能是7天中的 等。类似这种当一个变量有几种固定可能的取值时,就可以将它定义为枚举类型。
在JDK1.5之前没有枚举类型,那时候一般用接口常量来替代。而使用Java 枚举类型enum可以更贴近地表示这种常量。
1、声明枚举
2、声明枚举时必须使用enum关键字,然后定义枚举的名称、可访问属性、基础类型和成员等。
3、任意两个枚举成员不能具有相同的名称,且它的常数值必须在该枚举的基础类型的范围之内,多个枚举成员之间使用逗号分隔。
4、提示:如果没有显式地声明基础类型的枚举,那么意味着它所对应的基础类型是int。
在 Java 中一个异常的产生,主要有如下三种原因:
1、Java 内部错误发生异常,Java 虚拟机产生的异常。
2、编写的程序代码中的错误所产生的异常,例如空指针异常、数组越界异常等。
3、通过 throw 语句手动生成的异常,一般用来告知该方法的调用者一些必要信息。
异常类型
1、Exception 类用于用户程序可能出现的异常情况,它也是用来创建自定义异常类型类的类。
2、 2、Error 定义了在通常环境下不希望被程序捕获的异常。一般指的是 JVM 错误,如堆栈溢出。
如下是常见的 Error 和 Exception:
1)运行时异常(RuntimeException):
NullPropagation:空指针异常;
ClassCastException:类型强制转换异常
IllegalArgumentException:传递非法参数异常
IndexOutOfBoundsException:下标越界异常
NumberFormatException:数字格式异常
2)非运行时异常:
ClassNotFoundException:
找不到指定 class 的异常
IOException:IO 操作异常
3)错误(Error):
NoClassDefFoundError:找不到 class 定义异常
StackOverflowError:深递归导致栈被耗尽而抛出的异常
OutOfMemoryError:内存溢出异常
异常处理机制:
1、在方法中用 try catch 语句捕获并处理异常,catch 语句可以有多个,用来匹配多个异常。
2、对于处理不了的异常或者要转型的异常,在方法的声明处通过 throws 语句拋出异常,即由上层的调用方法来处理。
所以在默认情况下,日志只显示前三个级别,对于所有的级别有下面几种记录方法:
logger.warning(message);
logger.fine(message);
同时,还可以使用log方法指定级别,例如:
logger.log(Level.FINE, message);
System类的成员变量
System类有3个静态成员变量,分别是PrintStream out、InputStream in和PrintStream err。
arraycopy() 方法
该方法的作用是数组复制,即从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。该方法的具体定如下:
public static void arraycopy(object src,int srcPos,object dest,int destpos,int length)
其中,src表示源数组,srcPos 表示从源数组中复制的起始位置,dest 表示目标数组,destPos 表示要复制到的目标数组的起始位
讯置,length表示复制的个数。
Integer类的构造方法
Integer类中的构造方法有以下两个:
Integer(int value):构造一个新分配的Integer对象,它表示指定的int值。
Integer(String s):构造一个新分配的Integer对象,它表示Strig参数所指示的int值。
例如,以下代码分别使用以上两个构造方法来获取Integer对象:
Integer integer1 = new Integer(100); //以int 型变量作为参数创建 Integer 对象
Integer integer2 = new Integer(“100”); //以string 型变量作为参数创建Integer 对象
lnteger类包含以下4个常量。
MAX_VALUE:值为231-1的常量,它表示int类型能够表示的最大值。
MIN_VALUE:值为-231的常量,它表示int类型能够表示的最小值。
SIZE:用来以二进制补码形式表示igt值的比特位数。
TYPE:表示基本类型int的Class实例。
Float类的构造方法
Float类中的构造方法有以下3个。 +
1.Float(double value):构造一个新分配的Float对象,它表示转换为float类型的参数。
2.Float(float value):构造一个新分配的Float对象,它表示基本的float参数。
3.Float(String s):构造一个新分配的Float对象,它表示String参数所指示的float值。
Float类的常用常量
在Float类中包含了很多常量,其中较为常用的常量如下。
MAX_VALUE:值为1.4E38的常量,它表示float类型能够表示的最大值。MIN_VALUE:值为3.4E-45的常量,它表示float类型能够表示的最小值。MAX_EXPONENT:有限float变量可能具有的最大指数。
MIN_EXPONENT:标准化float变量可能具有的最小指数。
MIN_NORMAL:保存float类型数值的最小标准值的常量,即2-126。
NaN:保存float类型的非数字值的常量。
SIZE:用来以二进制补码形式表示float值的比特位数。
TYPE:表示基本类型float的Class实例。
自定义异常的语法形式为:
<自定义异常名>
一、String
1、分配一个新字符串,该字符串包含字符数组参数的子数组中的字符。
offset参数是子数组第一个字符的索引,count参数指定子数组的长度。
复制子数组的内容;字符数组的后续修改不会影响新创建的字符串。
2、分配一个新字符串,该字符串包含来自Unicode码点数组参数子数组的字符。
offset参数是子数组第一个编码点的索引,count参数指定子数组的长度。
子数组的内容被转换为字符;int数组的后续修改不会影响新创建的字符串。
3、将字符串转为int类型数据
注意:
将字符串转为int类型的值前提是字符内容必须是纯数字
使用包装类进行转换
4、Java中使用 “+” 运算符进行连接对程序的效率有很大的影响!
5、变量名.toUpperCase()和变量名.toLowerCase()
Jana中使用这两个方法对数组或字符串进行大小写转换。
subsString
此方法是对字符串进行截取。使用方法是:变量名.subsString(截取的起始位置,结束位置)。
date类
date类简介
获取系统当前时间
如果使用 DateFormat 类格式化日期/时间并不能满足要求,
那么就需要使用 DateFormat 类的子类——SimpleDateFormat。
/**
* Calendar 类
*/
// Calendar 类对象
Calendar cl = Calendar.getInstance();
// 获取一星期的第一天。根据不同的国家地区,返回不同的值
System.out.println(cl.getFirstDayOfWeek());
int year = cl.get(Calendar.YEAR); // 获取当前年份
System.out.println("现在是" + year + "年");
三、Math
1\Math类中常见的两个静态常量E、PI
System.out.println(“自然对数:” + Math.E);
System.out.println("圆周率:" + Math.PI);
2、math类中获取最大值和最小值
int a = 56;
int b = 23;
System.out.println("最大值为:" + Math.max(a, b));
System.out.println("最小值:" + Math.min(a, b));
四、Random类
Random 类提供了丰富的随机数生成方法,可以产生 boolean、int、long、float、byte
数组以及 double 类型的随机数,这是它与 Math.random() 方法最大的不同之处。random()
方法只能产生 double 类型的 0~1 的随机数。
五、数字格式化
实例化数字格式化对象,并且指定格式
DecimalFormat df1 = new DecimalFormat(“0.0”);
DecimalFormat df2 = new DecimalFormat("#.#");
DecimalFormat df3 = new DecimalFormat("000.000");
DecimalFormat df4 = new DecimalFormat("###.###");
六、java大数字运算
BigDecimal bd = new BigDecimal(3.14159263578461259445);
bd = bd.add(new BigDecimal(3.14159263578461259445));
System.out.println(bd);
七、内置类和包装类
1、装箱与拆箱
拆箱:将包装类转为基本数据类型
装箱:将基本数据类型转为包装类
- 实现 int 和 Integer 的相互转换
可以通过 Integer 类的构造方法将 int 装箱,通过 Integer 类的 intValue 方法将 Integer 拆箱。
- 将字符串转换为数值类型
在 Integer 和 Float 类中分别提供了以下两种方法:
① Integer 类(String 转 int 型)
int parseInt(String s);
s 为要转换的字符串。
② Float 类(String 转 float 型)
float parseFloat(String s)
注意:使用以上两种方法时,字符串中的数据必须由数字组成,否则转换时会出现程序错误。
3) 将整数转换为字符串
Integer 类有一个静态的 toString() 方法,可以将整数转换为字符串。
八、Object类
Object 是 Java 类库中的一个特殊类,也是所有类的父类。也就是说,Java 允许把任何类型的对象赋给 Object 类型的变量。当一个类被定义后,如果没有指定继承的父类,那么默认父类就是 Object 类。
提示:因为 Object 类可以接收任意的引用数据类型,所以在很多的类库设计上都采用 Object 作为方法的参数,这样操作起来会比较方便。
九、System类
System 类有 3 个静态成员变量,分别是 PrintStream out、InputStream in 和 PrintStream err。
1)PrintStream out
标准输出流。此流已打开并准备接收输出数据。通常,此流对应于显示器输出或者由主机环境或用户指定的另一个输出目标。例如,编写一行输出数据的典型方式是:System.out.println(data);
其中,println 方法是属于流类 PrintStream 的方法,而不是 System 中的方法。
2)InputStream in
标准输入流。此流已打开并准备提供输入数据。通常,此流对应于键盘输入或者由主机环境或用户指定的另一个输入源。
4)gc() 方法
该方法的作用是请求系统进行垃圾回收,完成内存中的垃圾清除。至于系统是否立刻回收,取决于系统中垃圾回收算法的实现以及系统执行时的情况。
5)getProperty() 方法
该方法的作用是获得系统中属性名为 key 的属性对应的值,
3)PrintStream err
标准的错误输出流。其语法与 System.out 类似,不需要提供参数就可输出错误信息。也可以用来输出用户指定的其他信息,包括变量的值。
十、JDK自带记录日志类
日志用来记录程序的运行轨迹,方便查找关键信息,也方便快速定位解决问题。下面介绍 Java 自带的日志工具类 java.util.logging 的使用。
Logger.getGlobal().info(“打印日志信息”);
int num = 32;
Logger.getGlobal().info(“定义了int类型的num变量,值为:” + num);
Logger.getGlobal().log(Level.WARNING, “这是警告信息”);
Logger.getGlobal().severe(“这是严重的情况”);
十一、HashSet
HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时就是使用这个实现类。HashSet 是按照 Hash 算法来存储集合中的元素。因此具有很好的存取和查找性能。
HashSet 具有以下特点:
1、不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。
2、HashSet 不是同步的,如果多个线程同时访问或修改一个 HashSet,则必须通过代码来保证其同步。
3、集合元素值可以是 null。
注意:我们说的set集合无序指的是存放元素的顺序与取元素的顺序不一致
十二、 collection接口
从集合中删除一个指定元素,当集合中包含了一个或多个元素 o 时,该方法只删除第一个符合条件的元素,该方法将返回 true。
1、利用forEach语法遍历集合元素
Java 8 为 Iterator 引入了一个 forEachRemaining(Consumer action) 默认方法,
该方法所需的 Consumer 参数同样也是函数式接口。当程序调用 Iterator 的
forEachRemaining(Consumer action) 遍历集合元素时,程序会依次将集合元素传给
Consumer 的 accept(T t) 方法(该接口中唯一的抽象方法)。
十三、 Collections 类
Collections 类是 Java 提供的一个操作 Set、List 和 Map 等集合的工具类。
Collections 类提供了许多操作集合的静态方法,借助这些静态方法可以实现集合元素的排序、
查找替换和复制等操作。
十四、try catch语句 (异常)
1、try catch语句,捕获异常并且处理异常
语法:
try{
代码块(可能出现错误的代码)
} catch(异常的类型){
处理异常的逻辑代码块
} catch () {
}.....
2、try catch finally语句
语法:
try{
可能出现异常的代码块
}catch(异常的类型){
处理异常的代码
}catch (异常的类型) {
处理异常的代码
}......{
}finally{
代码块(不管异常处理结果如何,最终都会走该语句块)
注意:该语句块是在方法结束之前调用的(return语句之前执行)
}
3、throws
当一个方法产生一个它不处理的异常时,那么就需要在该方法的头部声明这个异常,
以便将该异常传递到方法的外部进行处理。使用 throws 声明的方法表示此方法不处理异常
与 throws 不同的是,throw 语句用来直接拋出一个异常,后接一个可拋出的异常类对象
语法:
throw new 异常类型(异常信息声明)
4、如果 Java 提供的内置异常类型不能满足程序设计的需求,这时我们可以自己设计 Java 类库或框架,
其中包括异常类型。实现自定义异常类需要继承 Exception 类或其子类,如果自定义运行时异常类
需继承 RuntimeException 类或其子类。
十五、Map
Map 集合最典型的用法就是成对地添加、删除 key-value 对,接下来即可判断该
Map 中是否包含指定 key,也可以通过 Map 提供的 keySet() 方法获取所有 key
组成的集合,进而遍历 Map 中所有的 key-value 对。
map.remove(“python”, “new Date()”);
// 这是 Java8 新增的方法,删
// 除指定 key、value 所对应的 key-value 对。如果从该 Map 中成功地删除
// 该 key-value 对,该方法返回 true,否则返回 false。
十六、List类
1、ArrayList 类实现了可变数组的大小,存储在内的数据称为元素。
它还提供了快速基于索引访问元素的方式,对尾部成员的增加和删除支持较好。
使用 ArrayList 创建的集合,允许对集合中的元素进行快速的随机访问,
不过,向 ArrayList 中插入与删除元素的速度相对较慢。
2、 LinkedList 类采用链表结构保存对象,这种结构的优点是便于向集合中插入或者删除元素。
需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高,
但是 LinkedList 类随机访问元素的速度则相对较慢。这里的随机访问是指检索集合中特
定索引位置的元素。
# 十四、泛型
前面我们提到 Java 集合有个缺点,就是把一个对象“丢进”集合里之后,集合就会“忘记”这个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了 Object 类型(其运行时类型没变)。
Java 集合之所以被设计成这样,是因为集合的设计者不知道我们会用集合来保存什么类型的对象,所以他们把集合设计成能保存任何类型的对象,只要求具有很好的通用性,但这样做带来如下两个问题:
1、集合对元素类型没有任何限制,这样可能引发一些问题。例如,想创建一个只能保存 Dog 对象的集合,但程序也可以轻易地 将 Cat 对象“丢”进去,所以可能引发异常。
2、由于把对象“丢进”集合时,集合丢失了对象的状态信息,集合只知道它盛装的是 Object,因此取出集合元素后通常还需要进 行强制类型转换。这种强制类型转换既增加了编程的复杂度,也可能引发 ClassCastException 异常。
所以为了解决上述问题,从 Java 1.5 开始提供了泛型。泛型可以在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用率。
1、泛型集合
泛型本质上是提供类型的“类型参数”,也就是参数化类型。我们可以为类、接口或方法指定一个类型参数,通过这个参数限制操作的数据类型,从而保证类型转换的绝对安全。
例 1
下面将结合泛型与集合编写一个案例实现图书信息输出。
1)首先需要创建一个表示图书的实体类 Book,其中包括的图书信息有图书编号、图书名称和价格。Book 类的具体代码如下:
public class Book {
private int Id; // 图书编号
private String Name; // 图书名称
private int Price; // 图书价格
public Book(int id, String name, int price) { // 构造方法
this.Id = id;
this.Name = name;
this.Price = price;
}
public String toString() { // 重写 toString()方法
return this.Id + ", " + this.Name + "," + this.Price;
}
}
2、泛型类
除了可以定义泛型集合之外,还可以直接限定泛型类的类型参数。语法格式如下:
public class class_name<data_type1,data_type2,…>{}
其中,class_name 表示类的名称,data_ type1 等表示类型参数。Java 泛型支持声明一个以上的类型参数,只需要将类型用逗号隔开即可。
泛型类一般用于类中的属性类型不确定的情况下。在声明属性时,使用下面的语句:
private data_type1 property_name1;
private data_type2 property_name2;
该语句中的 data_type1 与类声明中的 data_type1 表示的是同一种数据类型。
3、泛型方法
到目前为止,我们所使用的泛型都是应用于整个类上。泛型同样可以在类中包含参数化的方法,而方法所在的类可以是泛型类,也可以不是泛型类。也就是说,是否拥有泛型方法,与其所在的类是不是泛型没有关系。
泛型方法使得该方法能够独立于类而产生变化。如果使用泛型方法可以取代类泛型化,那么就应该只使用泛型方法。另外,对一个 static 的方法而言,无法访问泛型类的类型参数。因此,如果 static 方法需要使用泛型能力,就必须使其成为泛型方法。
定义泛型方法的语法格式如下:
[访问权限修饰符] [static] [final] <类型参数列表> 返回值类型 方法名([形式参数列表])
例如:
public static <T> List find(Class<T> cs,int userId){}
一般来说编写 Java 泛型方法,其返回值类型至少有一个参数类型应该是泛型,而且类型应该是一致的,如果只有返回值类型或参数类型之一使用了泛型,那么这个泛型方法的使用就被限制了。下面就来定义一个泛型方法,具体介绍泛型方法的创建和使用。
4、泛型的高级用法
泛型的用法非常灵活,除在集合、类和方法中使用外,本节将从三个方面介绍泛型的高级用法,包括限制泛型可用类型、使用类型通配符、继承泛型类和实现泛型接口。
1. 限制泛型可用类型
在 Java 中默认可以使用任何类型来实例化一个泛型类对象。当然也可以对泛型类实例的类型进行限制,语法格式如下:
class 类名称<T extends anyClass>
其中,anyClass 指某个接口或类。使用泛型限制后,泛型类的类型必须实现或继承 anyClass 这个接口或类。无论 anyClass 是接口还是类,在进行泛型限制时都必须使用 extends 关键字。
例如,在下面的示例代码中创建了一个 ListClass 类,并对该类的类型限制为只能是实现 List 接口的类。
// 限制ListClass的泛型类型必须实现List接口
public class ListClass<T extends List> {
public static void main(String[] args) {
// 实例化使用ArrayList的泛型类ListClass,正确
ListClass<ArrayList> lc1 = new ListClass<ArrayList>();
// 实例化使用LinkedList的泛型类LlstClass,正确
ListClass<LinkedList> lc2 = new ListClass<LinkedList>();
// 实例化使用HashMap的泛型类ListClass,错误,因为HasMap没有实现List接口
// ListClass<HashMap> lc3=new ListClass<HashMap>();
}
}
在上述代码中,定义 ListClass 类时设置泛型类型必须实现 List 接口。例如,ArrayList 和 LinkedList 都实现了 List 接口,所以可以实例化 ListClass 类。而 HashMap 没有实现 List 接口,所以在实例化 ListClass 类时会报错。
3. 继承泛型类和实现泛型接口
定义为泛型的类和接口也可以被继承和实现。例如下面的示例代码演示了如何继承泛型类。
public class FatherClass<T1>{}
public class SonClass<T1,T2,T3> extents FatherClass<T1>{}
如果要在 SonClass 类继承 FatherClass 类时保留父类的泛型类型,需要在继承时指定,否则直接使用 extends FatherClass 语句进行继承操作,此时 T1、T2 和 T3 都会自动变为 Object,所以一般情况下都将父类的泛型类型保留。
I/O 流
一、流的概念
在 Java 中所有数据都是使用流读写的。流是一组有序的数据序列,将数据从一个地方带到另一个地方。根据数据流向的不同,可以分为输入(Input)流和输出(Output)流两种。
在学习输入和输出流之前,我们要明白为什么应用程序需要输入和输出流。例如,我们平时用的 Office 软件,对于 Word、Excel 和 PPT 文件,我们需要打开文件并读取这些文本,和编辑输入一些文本,这都需要利用输入和输出的功能。在现实生活中,输入和输出的例子比比皆是。
在前面章节中,我们经常使用“System.out.println”方法,它就是一个输出方法。后面我们会讲述更多的方法,并通过大量的实例来展示它们的用法。
1、什么是输入/输出流
Java 程序通过流来完成输入/输出,所有的输入/输出以流的形式处理。因此要了解 I/O 系统,首先要理解输入/输出流的概念。
输入就是将数据从各种输入设备(包括文件、键盘等)中读取到内存中,输出则正好相反,是将数据写入到各种输出设备(比如文件、显示器、磁盘等)。例如键盘就是一个标准的输入设备,而显示器就是一个标准的输出设备,但是文件既可以作为输入设备,又可以作为输出设备。
数据流是 Java 进行 I/O 操作的对象,它按照不同的标准可以分为不同的类别。
1、按照流的方向主要分为输入流和输出流两大类。
2、数据流按照数据单位的不同分为字节流和字符流。
3、按照功能可以划分为节点流和处理流。
数据流的处理只能按照数据序列的顺序来进行,即前一个数据处理完之后才能处理后一个数据。数据流以输入流的形式被程序获取,再以输出流的形式将数据输出到其它设备。图 1 为输入流模式,图 2 为输出流模式。
2、输入流
Java 流相关的类都封装在 java.io 包中,而且每个数据流都是一个对象。所有输入流类都是 InputStream 抽象类(字节输入流)和 Reader 抽象类(字符输入流)的子类。其中 InputStream 类是字节输入流的抽象类,是所有字节输入流的父类,其层次结构如图 3 所示。
InputStream 类中所有方法遇到错误时都会引发 IOException 异常。如下是该类中包含的常用方法。
名称 | 作用 |
---|---|
int read() | 从输入流读入一个 8 字节的数据,将它转换成一个 0~ 255 的整数,返回一个整数,如果遇到输入流的结尾返回 -1 |
int read(byte[] b) | 从输入流读取若干字节的数据保存到参数 b 指定的字节数组中,返回的字节数表示读取的字节数,如果遇到输入流的结尾返回 -1 |
int read(byte[] b,int off,int len) | 从输入流读取若干字节的数据保存到参数 b 指定的字节数组中,其中 off 是指在数组中开始保存数据位置的起始下标,len 是指读取字节的位数。返回的是实际读取的字节数,如果遇到输入流的结尾则返回 -1 |
void close() | 关闭数据流,当完成对数据流的操作之后需要关闭数据流 |
int available() | 返回可以从数据源读取的数据流的位数。 |
skip(long n) | 从输入流跳过参数 n 指定的字节数目 |
boolean markSupported() | 判断输入流是否可以重复读取,如果可以就返回 true |
void mark(int readLimit) | 如果输入流可以被重复读取,从流的当前位置开始设置标记,readLimit 指定可以设置标记的字节数 |
void reset() | 使输入流重新定位到刚才被标记的位置,这样可以重新读取标记过的数据 |
上述最后 3 个方法一般会结合在一起使用,首先使用 markSupported() 判断,如果可以重复读取,则使用 mark(int readLimit) 方法进行标记,标记完成之后可以使用 read() 方法读取标记范围内的字节数,最后使用 reset() 方法使输入流重新定位到标记的位置,继而完成重复读取操作。
Java 中的字符是 Unicode 编码,即双字节的,而 InputerStream 是用来处理单字节的,在处理字符文本时不是很方便。这时可以使用 Java 的文本输入流 Reader 类,该类是字符输入流的抽象类,即所有字符输入流的实现都是它的子类,该类的方法与 InputerSteam 类的方法类似,这里不再介绍。
3、输出流
在 Java 中所有输出流类都是 OutputStream 抽象类(字节输出流)和 Writer 抽象类(字符输出流)的子类。其中 OutputStream 类是字节输出流的抽象类,是所有字节输出流的父类,其层次结构如图 4 所示。
OutputStream 类是所有字节输出流的超类,用于以二进制的形式将数据写入目标设备,该类是抽象类,不能被实例化。OutputStream 类提供了一系列跟数据输出有关的方法,如下所示。
名称 | 作用 |
---|---|
int write(b) | 将指定字节的数据写入到输出流 |
int write (byte[] b) | 将指定字节数组的内容写入输出流 |
int write (byte[] b,int off,int len) | 将指定字节数组从 off 位置开始的 len 字节的内容写入输出流 |
close() | 关闭数据流,当完成对数据流的操作之后需要关闭数据流 |
flush() | 刷新输出流,强行将缓冲区的内容写入输出流 |
二、系统流
每个 Java 程序运行时都带有一个系统流,系统流对应的类为 java.lang.System。Sytem 类封装了 Java 程序运行时的 3 个系统流,分别通过 in、out 和 err 变量来引用。这 3 个系统流如下所示:
**System.in:**标准输入流,默认设备是键盘。
**System.out:**标准输出流,默认设备是控制台。
**System.err:**标准错误流,默认设备是控制台。
以上变量的作用域为 public 和 static,因此在程序的任何部分都不需引用 System 对象就可以使用它们。
例 1
下面的程序演示了如何使用 System.in 读取字节数组,使用 System.out 输出字节数组。
public class Test01 {
public static void main(String[] args) {
byte[] byteData = new byte[100]; // 声明一个字节数组
System.out.println("请输入英文:");
try {
System.in.read(byteData);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("您输入的内容如下:");
for (int i = 0; i < byteData.length; i++) {
System.out.print((char) byteData[i]);
}
}
}
该程序的运行结果如下所示:
请输入英文:
abcdefg hijklmn opqrst uvwxyz
您输入的内容如下:
abcdefg hijklmn opqrst uvwxyz
System.in 是 InputStream 类的一个对象,因此上述代码的 System.in.read() 方法实际是访问 InputStream 类定义的 read() 方法。该方法可以从键盘读取一个或多个字符。对于 System.out 输出流主要用于将指定内容输出到控制台。
System.out 和 System.error 是 PrintStream 类的对象。因为 PrintStream 是一个从 OutputStream 派生的输出流,所以它还执行低级别的 write() 方法。因此,除了 print() 和 println() 方法可以完成控制台输出以外,System.out 还可以调用 write() 方法实现控制台输出。
write() 方法的简单形式如下:
void write(int byteval) throws IOException
该方法通过 byteval 参数向文件写入指定的字节。在实际操作中,print() 方法和 println() 方法比 write() 方法更常用。
**注意:**尽管它们通常用于对控制台进行读取和写入字符,但是这些都是字节流。因为预定义流是没有引入字符流的 Java 原始规范的一部分,所以它们不是字符流而是字节流,但是在 Java 中可以将它们打包到基于字符的流中使用。
、File类(文件操作类)
1、File类简介
在 Java 中,File 类是 java.io 包中唯一代表磁盘文件本身的对象,也就是说,如果希望在程序中操作文件和目录,则都可以通过 File 类来完成。File 类定义了一些方法来操作文件,如新建、删除、重命名文件和目录等。
File 类不能访问文件内容本身,如果需要访问文件内容本身,则需要使用输入/输出流。
File 类提供了如下三种形式构造方法。
**File(String path):**如果 path 是实际存在的路径,则该 File 对象表示的是目录;如果 path 是文件名,则该 File 对象表示的是文 件。
**File(String path, String name):**path 是路径名,name 是文件名。
**File(File dir, String name):**dir 是路径对象,name 是文件名。
使用任意一个构造方法都可以创建一个 File 对象,然后调用其提供的方法对文件进行操作。在下表中列出了 File 类的常用方法及说明。
方法名称 | 说明 |
---|---|
boolean canRead() | 测试应用程序是否能从指定的文件中进行读取 |
boolean canWrite() | 测试应用程序是否能写当前文件 |
boolean delete() | 删除当前对象指定的文件 |
boolean exists() | 测试当前 File 是否存在 |
String getAbsolutePath() | 返回由该对象表示的文件的绝对路径名 |
String getName() | 返回表示当前对象的文件名或路径名(如果是路径,则返回最后一级子路径名) |
String getParent() | 返回当前 File 对象所对应目录(最后一级子目录)的父目录名 |
boolean isAbsolute() | 测试当前 File 对象表示的文件是否为一个绝对路径名。该方法消除了不同平台的差异,可以直接判断 file 对象是否为绝对路径。在 UNIX/Linux/BSD 等系统上,如果路径名开头是一条斜线/ ,则表明该 File 对象对应一个绝对路径;在 Windows 等系统上,如果路径开头是盘符,则说明它是一个绝对路径。 |
boolean isDirectory() | 测试当前 File 对象表示的文件是否为一个路径 |
boolean isFile() | 测试当前 File 对象表示的文件是否为一个“普通”文件 |
long lastModified() | 返回当前 File 对象表示的文件最后修改的时间 |
long length() | 返回当前 File 对象表示的文件长度 |
String[] list() | 返回当前 File 对象指定的路径文件列表 |
String[] list(FilenameFilter) | 返回当前 File 对象指定的目录中满足指定过滤器的文件列表 |
boolean mkdir() | 创建一个目录,它的路径名由当前 File 对象指定 |
boolean mkdirs() | 创建一个目录,它的路径名由当前 File 对象指定 |
boolean renameTo(File) | 将当前 File 对象指定的文件更名为给定参数 File 指定的路径名 |
File 类中有以下两个常用常量:
**public static final String pathSeparator:**指的是分隔连续多个路径字符串的分隔符,Windows 下指;。例如 java -cp test.jar;abc.jar HelloWorld。
**public static final String separator:**用来分隔同一个路径字符串中的目录的,Windows 下指/。例如 C:/Program Files/Common Files。
**注意:**可以看到 File 类的常量定义的命名规则不符合标准命名规则,常量名没有全部大写,这是因为 Java 的发展经过了一段相当长的时间,而命名规范也是逐步形成的,File 类出现较早,所以当时并没有对命名规范有严格的要求,这些都属于 Java 的历史遗留问题。
Windows 的路径分隔符使用反斜线“\”,而 Java 程序中的反斜线表示转义字符,所以如果需要在 Windows 的路径下包括反斜线,则应该使用两条反斜线或直接使用斜线“/”也可以。Java 程序支持将斜线当成平台无关的路径分隔符。
假设在 Windows 操作系统中有一文件 D:\javaspace\hello.java
,在 Java 中使用的时候,其路径的写法应该为 D:/javaspace/hello.java
或者 D:\\javaspace\\hello.java
。
2、获取文件属性
在 Java 中获取文件属性信息的第一步是先创建一个 File 类对象并指向一个已存在的文件, 然后调用表 1 中的方法进行操作。
例 1
假设有一个文件位于 C:\windows\notepad.exe
。编写 Java 程序获取并显示该文件的长度、是否可写、最后修改日期以及文件路径等属性信息。实现代码如下:
public class Test02 {
public static void main(String[] args) {
String path = "C:/windows/"; // 指定文件所在的目录
File f = new File(path, "notepad.exe"); // 建立File变量,并设定由f变量引用
System.out.println("C:\\windows\\notepad.exe文件信息如下:");
System.out.println("============================================");
System.out.println("文件长度:" + f.length() + "字节");
System.out.println("文件或者目录:" + (f.isFile() ? "是文件" : "不是文件"));
System.out.println("文件或者目录:" + (f.isDirectory() ? "是目录" : "不是目录"));
System.out.println("是否可读:" + (f.canRead() ? "可读取" : "不可读取"));
System.out.println("是否可写:" + (f.canWrite() ? "可写入" : "不可写入"));
System.out.println("是否隐藏:" + (f.isHidden() ? "是隐藏文件" : "不是隐藏文件"));
System.out.println("最后修改日期:" + new Date(f.lastModified()));
System.out.println("文件名称:" + f.getName());
System.out.println("文件路径:" + f.getPath());
System.out.println("绝对路径:" + f.getAbsolutePath());
}
}
在上述代码中 File 类构造方法的第一个参数指定文件所在位置,这里使用C:/
作为文件的实际路径;第二个参数指定文件名称。创建的 File 类对象为 f,然后通过 f 调用方法获取相应的属性,最终运行效果如下所示。
C:\windows\notepad.exe文件信息如下:
============================================
文件长度:193536字节
文件或者目录:是文件
文件或者目录:不是目录
是否可读:可读取
是否可写:可写入
是否隐藏:不是隐藏文件
最后修改日期:Mon Dec 28 02:55:19 CST 2022
文件名称:notepad.exe
文件路径:C:\windows\notepad.exe
绝对路径:C:\windows\notepad.exe
3、创建和删除文件
File 类不仅可以获取已知文件的属性信息,还可以在指定路径创建文件,以及删除一个文件。创建文件需要调用 createNewFile() 方法,删除文件需要调用 delete() 方法。无论是创建还是删除文件通常都先调用 exists() 方法判断文件是否存在。
例 2
假设要在 C 盘上创建一个 test.txt 文件,程序启动时会检测该文件是否存在,如果不存在则创建;如果存在则删除它再创建。
实现代码如下:
public class Test03 {
public static void main(String[] args) throws IOException {
File f = new File("C:\\test.txt"); // 创建指向文件的File对象
if (f.exists()) // 判断文件是否存在
{
f.delete(); // 存在则先删除
}
f.createNewFile(); // 再创建
}
}
运行程序之后可以发现,在 C 盘中已经创建好了 test.txt 文件。但是如果在不同的操作系统中,路径的分隔符是不一样的,例如:
1、Windows 中使用反斜杠\表示目录的分隔符。
2、Linux 中使用正斜杠/表示目录的分隔符。
那么既然 Java 程序本身具有可移植性的特点,则在编写路径时最好可以根据程序所在的操作系统自动使用符合本地操作系统要求的分隔符,这样才能达到可移植性的目的。要实现这样的功能,则就需要使用 File 类中提供的两个常量。
代码修改如下:
public static void main(String[] args) throws IOException {
String path = "C:" + File.separator + "test.txt"; // 拼凑出可以适应操作系统的路径
File f = new File(path);
if (f.exists()) // 判断文件是否存在
{
f.delete(); // 存在则先删除
}
f.createNewFile(); // 再创建
}
程序的运行结果和前面程序一样,但是此时的程序可以在任意的操作系统中使用。
**注意:**在操作文件时一定要使用 File.separator 表示分隔符。在程序的开发中,往往会使用 Windows 开发环境,因为在 Windows 操作系统中支持的开发工具较多,使用方便,而在程序发布时往往是直接在 Linux 或其它操作系统上部署,所以这时如果不使用 File.separator,则程序运行就有可能存在问题。关于这一点我们在以后的开发中一定要有所警惕。
4、创建和删除目录
File 类除了对文件的创建和删除外,还可以创建和删除目录。创建目录需要调用 mkdir() 方法,删除目录需要调用 delete() 方法。无论是创建还是删除目录都可以调用 exists() 方法判断目录是否存在。
例 3
编写一个程序判断 C 盘根目录下是否存在 config 目录,如果存在则先删除再创建。实现代码如下:
public class Test04 {
public static void main(String[] args) {
String path = "C:/config/"; // 指定目录位置
File f = new File(path); // 创建File对象
if (f.exists()) {
f.delete();
}
f.mkdir(); // 创建目录
}
}
5、遍历目录
通过遍历目录可以在指定的目录中查找文件,或者显示所有的文件列表。File 类的 list() 方法提供了遍历目录功能,该方法有如下两种重载形式。
1. String[] list()
该方法表示返回由 File 对象表示目录中所有文件和子目录名称组成的字符串数组,如果调用的 File 对象不是目录,则返回 null。
**提示:**list() 方法返回的数组中仅包含文件名称,而不包含路径。但不保证所得数组中的相同字符串将以特定顺序出现,特别是不保证它们按字母顺序出现。
2. String[] list(FilenameFilter filter)
该方法的作用与 list() 方法相同,不同的是返回数组中仅包含符合 filter 过滤器的文件和目录,如果 filter 为 null,则接受所有名称。
例 4
假设要遍历 C 盘根目录下的所有文件和目录,并显示文件或目录名称、类型及大小。使用 list() 方法的实现代码如下:
public class Test05 {
public static void main(String[] args) {
File f = new File("C:/"); // 建立File变量,并设定由f变量变数引用
System.out.println("文件名称\t\t文件类型\t\t文件大小");
System.out.println("===================================================");
String fileList[] = f.list(); // 调用不带参数的list()方法
for (int i = 0; i < fileList.length; i++) { // 遍历返回的字符数组
System.out.print(fileList[i] + "\t\t");
System.out.print((new File("C:/", fileList[i])).isFile() ? "文件" + "\t\t" : "文件夹" + "\t\t");
System.out.println((new File("C:/", fileList[i])).length() + "字节");
}
}
}
由于 list() 方法返回的字符数组中仅包含文件名称,因此为了获取文件类型和大小,必须先转换为 File 对象再调用其方法。如下所示的是实例的运行效果:
文件名称 文件类型 文件大小
===================================================
$Recycle.Bin 文件夹 4096字节
Documents and Settings 文件夹 0字节
Download 文件夹 0字节
DRIVERS 文件夹 0字节
FibocomLog 文件夹 0字节
Gateface 文件夹 0字节
GFPageCache 文件夹 0字节
hiberfil.sys 文件 3375026176字节
Intel 文件夹 0字节
KuGou 文件夹 0字节
logs 文件夹 0字节
msdia80.dll 文件 904704字节
MSOCache 文件夹 0字节
MyDownloads 文件夹 0字节
MyDrivers 文件夹 0字节
news.template 文件 417字节
NVIDIA 文件夹 0字节
OneDriveTemp 文件夹 0字节
opt 文件夹 0字节
pagefile.sys 文件 6442450944字节
PerfLogs 文件夹 0字节
Program Files 文件夹 12288字节
Program Files (x86) 文件夹 8192字节
ProgramData 文件夹 12288字节
QMDownload 文件夹 0字节
Recovery 文件夹 0字节
swapfile.sys 文件 268435456字节
System Volume Information 文件夹 12288字节
Users 文件夹 4096字节
Windows 文件夹 16384字节
例 5
假设希望只列出目录下的某些文件,这就需要调用带过滤器参数的 list() 方法。首先需要创建文件过滤器,该过滤器必须实现 java.io.FilenameFilter
接口,并在 accept() 方法中指定允许的文件类型。
如下所示为允许 SYS、TXT 和 BAK 格式文件的过滤器实现代码:
public class ImageFilter implements FilenameFilter {
// 实现 FilenameFilter 接口
@Override
public boolean accept(File dir, String name) {
// 指定允许的文件类型
return name.endsWith(".sys") || name.endsWith(".txt") || name.endsWith(".bak");
}
}
上述代码创建的过滤器名称为 ImageFilter,接下来只需要将该名称传递给 list() 方法即可实现筛选文件。如下所示为修改后的 list() 方法,其他代码与例 4 相同,这里不再重复。
String fileList[] = f.list(new ImageFilter());
再次运行程序,遍历结果如下所示:
文件名称 文件类型 文件大小
===================================================
offline_FtnInfo.txt 文件 296字节
pagefile.sys 文件 8436592640字节
序列化与反序列化
一、基本概念
1、序列化和反序列化的定义:
(1)Java序列化就是指把Java对象转换为字节序列的过程
Java反序列化就是指把字节序列恢复为Java对象的过程。
(2)序列化最重要的作用:在传递和保存对象时.保证对象的完整性和可传递性。对象转换为有序字节流,以便在网络上传输或者保存在本地文件中。
反序列化的最重要的作用:根据字节流中保存的对象状态及描述信息,通过反序列化重建对象。
总结:核心作用就是对象状态的保存和重建。(整个过程核心点就是字节流中所保存的对象状态及描述信息)
2、json/xml的数据传递:
在数据传输(也可称为网络传输)前,先通过序列化工具类将Java对象序列化为json/xml文件。
在数据传输(也可称为网络传输)后,再将json/xml文件反序列化为对应语言的对象
3、序列化优点:
①将对象转为字节流存储到硬盘上,当JVM停机的话,字节流还会在硬盘上默默等待,等待下一次JVM的启动,把序列化的对象,通过反序列化为原来的对象,并且序列化的二进制序列能够减少存储空间(永久性保存对象)。
②序列化成字节流形式的对象可以进行网络传输(二进制形式),方便了网络传输。
③通过序列化可以在进程间传递对象。
4、序列化算法需要做的事:
① 将对象实例相关的类元数据输出。
② 递归地输出类的超类描述直到不再有超类。
③ 类元数据输出完毕后,从最顶端的超类开始输出对象实例的实际数据值。
④ 从上至下递归输出实例的数据。
二、Java实现序列化和反序列化的过程
1、实现序列化的必备要求:
只有实现了Serializable或者Externalizable接口的类的对象才能被序列化为字节序列。(不是则会抛出异常)
2、JDK中序列化和反序列化的API:
①java.io.ObjectInputStream:对象输入流。
该类的readObject()方法从输入流中读取字节序列,然后将字节序列反序列化为一个对象并返回。
②java.io.ObjectOutputStream:对象输出流。
该类的writeObject(Object obj)方法将将传入的obj对象进行序列化,把得到的字节序列写入到目标输出流中进行输出。
3、实现序列化和反序列化的三种实现:
①若Student类仅仅实现了Serializable接口,则可以按照以下方式进行序列化和反序列化。
ObjectOutputStream采用默认的序列化方式,对Student对象的非transient的实例变量进行序列化。
ObjcetInputStream采用默认的反序列化方式,对Student对象的非transient的实例变量进行反序列化。
②若Student类仅仅实现了Serializable接口,并且还定义了readObject(ObjectInputStream in)和writeObject(ObjectOutputSteam out),则采用以下方式进行序列化与反序列化。
ObjectOutputStream调用Student对象的writeObject(ObjectOutputStream out)的方法进行序列化。
ObjectInputStream会调用Student对象的readObject(ObjectInputStream in)的方法进行反序列化。
③若Student类实现了Externalnalizable接口,且Student类必须实现readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法,则按照以下方式进行序列化与反序列化。
ObjectOutputStream调用Student对象的writeExternal(ObjectOutput out))的方法进行序列化。
ObjectInputStream会调用Student对象的readExternal(ObjectInput in)的方法进行反序列化。
4、序列化和反序列化代码示例
public class SerializableTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//序列化
FileOutputStream fos = new FileOutputStream(“object.out”);
ObjectOutputStream oos = new ObjectOutputStream(fos);
Student student1 = new Student(“lihao”, “wjwlh”, “21”);
oos.writeObject(student1);
oos.flush();
oos.close();
//反序列化
FileInputStream fis = new FileInputStream(“object.out”);
ObjectInputStream ois = new ObjectInputStream(fis);
Student student2 = (Student) ois.readObject();
System.out.println(student2.getUserName()+ " " +
student2.getPassword() + " " + student2.getYear());
}
}
public class Student implements Serializable{
private static final long serialVersionUID = -6060343040263809614L;
private String userName;
private String password;
private String year;
public String getUserName() {
return userName;
}
public String getPassword() {
return password;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setPassword(String password) {
this.password = password;
}
public String getYear() {
return year;
}
public void setYear(String year) {
this.year = year;
}
public Student(String userName, String password, String year) {
this.userName = userName;
this.password = password;
this.year = year;
}
}
序列化和反序列化的注意点:
①序列化时,只对对象的状态进行保存,而不管对象的方法;
②当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;
③当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;
④并非所有的对象都可以序列化,至于为什么不可以,有很多原因了,比如:
安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行RMI传输等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的;
资源分配方面的原因,比如socket,thread类,如果可以序列化,进行传输或者保存,也无法对他们进行重新的资源分配,而且,也是没有必要这样实现;
⑤声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态,transient代表对象的临时数据。
⑥序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。为它赋予明确的值。显式地定义serialVersionUID有两种用途:
在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;
在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。
⑦Java有很多基础类已经实现了serializable接口,比如String,Vector等。但是也有一些没有实现serializable接口的;
⑧如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存!这是能用序列化解决深拷贝的重要原因;
注意:浅拷贝请使用Clone接口的原型模式。
实现 Runnable 接口
说明
java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时在用继承Thread类方法来创建线程显然不可能了。
java设计者们提供了另外一个方式创建线程,就是通过实现Runnable接口来创建线程
应用案例
请编写程序,该程序可以每隔1秒。在控制台输出“你好,旅行者”,当输出10次后,自动退出。
使用实现Runnable接口的方式实现。
public class Thread02 {
public static void main(String[] args) {
Dog dog = new Dog();
//dog.start(); //这里不能调用 start 方法
//创建了 Thread 对象,把 dog 对象(实现了 Runnable ),放入了 Thread
Thread thread = new Thread(dog);
thread.start();
}
}
class Dog implements Runnable { //通过实现Runnable接口来实现
int count = 0;
@Override
public void run() { //普通方法
while (true) {
System.out.println("你好,旅行者-" + (++count) + Thread.currentThread().getName());
//休眠1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 10) {
break;
}
}
}
}
多线程简介
1、简介
多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理” [1] 。
2、多线程使用场景:
后台任务:
如定时向大量(100W以上)的用户发送邮件
定期更新配置文件
任务调度(如quartz)
一些监控用于定期信息采集
自动作业处理:比如定期备份日志、定期备份数据库。
异步处理:如发微博、记录日志。
页面异步处理:比如大批量数据的核对工作(有10万个手机号码,核对哪些是已有用户)。
数据库的数据分析(待分析的数据太多),数据迁移。
线程的休眠、线程的优先级
1.1 线程的休眠
如果要想要让某些线程延缓执行,那么就可以使用使用休眠的方法来进行处理,在Thread类里面提供如下休眠操作:
休眠方法:public static void sleep(long millis) throws InterruptedException,如果休眠时间没有到就停止了休眠,那么就会产生中断异常。
1.2 线程的优先级
从理论上讲,优先级越高的线程越有可能先执行,而在Thread类中定义有一下的优先级的操作方法。
设置优先级:public final void setPriority(int newPriority)
取得优先级:public final int getPriority()
而对于优先级一共定义了三种:
最高优先级:public static final int MAX_PRIORITY 10
中等优先级:public static final int NORM_PRIORITY 5
最低优先级:public static final int MIN_PRIORITY 1
总结:
1.线程要有名字,Thread.currentThread()取得当前线程;
2.线程的休眠是有先后顺序的;
3.理论上线程的优先级越高的越有可能先执行
Java线程分为用户线程和守护线程。
守护线程是程序运行的时候在后台提供一种通用服务的线程。所有用户线程停止,进程会停掉所有守护线程,退出程序。
Java中把线程设置为守护线程的方法:在 start 线程之前调用线程的 setDaemon(true) 方法。
注意:
setDaemon(true) 必须在 start() 之前设置,否则会抛出IllegalThreadStateException异常,该线程仍默认为用户线程,继续执行
守护线程创建的线程也是守护线程
守护线程不应该访问、写入持久化资源,如文件、数据库,因为它会在任何时间被停止,导致资源未释放、数据写入中断等问题
class TimerThread extends Thread {
@Override
public void run() {
while (true) {
System.out.println(LocalTime.now());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
}
}
中断线程
中断线程
如果线程需要执行一个长时间任务,就可能需要能中断线程。中断线程就是其他线程给该线程发一个信号,该线程收到信号后结束执行run()
方法,使得自身线程能立刻结束运行。
我们举个栗子:假设从网络下载一个100M的文件,如果网速很慢,用户等得不耐烦,就可能在下载过程中点“取消”,这时,程序就需要中断下载线程的执行。
中断一个线程非常简单,只需要在其他线程中对目标线程调用interrupt()
方法,目标线程需要反复检测自身状态是否是interrupted状态,如果是,就立刻结束运行。
lock锁
lock是一个接口,里面只定义了lock、trylock、unlock等方法,所以实现原理我们直接从ReentrentLock来看。ReentrantLock把所有Lock接口的操作都委派到一个Sync类上,该类继承了AbstractQueuedSynchronizer(简称AQS),线程使用ReentrantLock获取锁分为两个阶段,第一个阶段是初次竞争(ReentrantLock默认使用非公平锁,当我们调用ReentrantLock的lock方法的时候,实际上它调用的是非公平锁的lock(),这个方法先用CAS操作,去尝试抢占该锁。如果成功,就把当前线程设置在这个锁上,表示抢占成功,如果失败,就调用LockSupport.park将当前线程阻塞,将其加入CLH队列中,等待抢占),第二个阶段是基于CLH队列的竞争。(然后进入CLH队列的抢占模式,当持有锁的那个线程调用unlock的时候,会将CLH队列的头结点的下一个节点线程唤醒,调用的是LockSupport.unpark()方法。)在初次竞争的时候是否考虑队列节点直接区分出了公平锁和非公平锁。在基于CLH队列的锁竞争中,依靠CAS操作来抢占锁,依靠LockSupport来做线程的挂起和唤醒,使用队列来保证并发执行变成了串行执行,从而消除了并发所带来的问题。总体来说,ReentrantLock是一个比较轻量级的锁,而且使用面向对象的思想去实现了锁的功能,比原来的synchronized关键字更加好理解。
对于卖票的方法,我们可以这样写,模拟卖票,打印出线程名,加上卖的票的编号
public class LockDemo implements Runnable{
public static void main(String[] args) {
study.study03.LockDemo lockDemo=new study.study03.LockDemo();
new Thread(lockDemo,"窗口1").start();
new Thread(lockDemo,"窗口2").start();
new Thread(lockDemo,"窗口3").start();
new Thread(lockDemo,"窗口4").start();
}
private static int count =100;
private Lock lock=new ReentrantLock();
@Override
public void run() {
while (count>0){
sellTickets();
}
}
private void sellTickets() {
try{
Thread.sleep(30);
}catch (Exception e){
e.printStackTrace();
}
try{
lock.lock();//上锁
if(count >0){
System.out.println(Thread.currentThread().getName()+",正在出售:"+(100-count+1));
count--;
}
}catch (Exception e){
e.getCause();
}finally {
lock.unlock();//释放锁
}
}
}
线程池
1 线程池的优势
2 线程池的使用
3 线程池的工作原理
4 线程池的参数
4.1 任务队列(workQueue)
4.2 线程工厂(threadFactory)
4.3 拒绝策略(handler)
5 功能线程池
5.1 定长线程池(FixedThreadPool)
5.2 定时线程池(ScheduledThreadPool )
5.3 可缓存线程池(CachedThreadPool)
5.4 单线程化线程池(SingleThreadExecutor)
5.5 对比
6 总结
1 线程池的优势
总体来说,线程池有如下的优势:
(1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
(2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
(3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
Socket 编程
整理和总结了一下大家常遇到的问题:
- 客户端socket发送消息后,为什么服务端socket没有收到?
2. 使用while 循环实现连续输入,是不是就是多线程模式?
3. 对多线程处理机制不是很明白,希望详细讲解?
4. 希望详细讲解ServerSocketChannel和SocketChannel与ServerSoket和Socket的区别?
5. 希望有详细的例子,可以直接拷贝下来运行?
## 2、ServerSocket 类的方法
服务器应用程序通过使用 java.net.ServerSocket 类以获取一个端口,并且侦听客户端请求。ServerSocket 类有四个构造方法:
方法 | 方法描述 |
---|---|
public ServerSocket(int port) throws IOException | 创建绑定到特定端口的服务器套接字。 |
public ServerSocket(int port, int backlog) throws IOException | 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。 |
public ServerSocket(int port, int backlog, InetAddress address) throws IOException | 使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。 |
public ServerSocket() throws IOException | 创建非绑定服务器套接字。 |
创建非绑定服务器套接字。 如果 ServerSocket 构造方法没有抛出异常,就意味着你的应用程序已经成功绑定到指定的端口,并且侦听客户端请求。
这里有一些 ServerSocket 类的常用方法:
方法 | 方法描述 |
---|---|
public int getLocalPort() | 返回此套接字在其上侦听的端口。 |
public Socket accept() throws IOException | 侦听并接受到此套接字的连接。 |
public void setSoTimeout(int timeout) | 通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位。 |
public void bind(SocketAddress host, int backlog) | 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。 |
3、Socket 类的方法
java.net.Socket 类代表客户端和服务器都用来互相沟通的套接字。客户端要获取一个 Socket 对象通过实例化 ,而 服务器获得一个 Socket 对象则通过 accept() 方法的返回值。
Socket 类有五个构造方法.
方法 | 方法描述 |
---|---|
public Socket(String host, int port) throws UnknownHostException, IOException. | 创建一个流套接字并将其连接到指定主机上的指定端口号。 |
public Socket(InetAddress host, int port) throws IOException | 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 |
public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException. | 创建一个套接字并将其连接到指定远程主机上的指定远程端口。 |
public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException. | 创建一个套接字并将其连接到指定远程地址上的指定远程端口。 |
public Socket() | 通过系统默认类型的 SocketImpl 创建未连接套接字 |
当 Socket 构造方法返回,并没有简单的实例化了一个 Socket 对象,它实际上会尝试连接到指定的服务器和端口。
下面列出了一些感兴趣的方法,注意客户端和服务器端都有一个 Socket 对象,所以无论客户端还是服务端都能够调用这些方法。
方法 | 方法描述 |
---|---|
public void connect(SocketAddress host, int timeout) throws IOException | 将此套接字连接到服务器,并指定一个超时值。 |
public InetAddress getInetAddress() | 返回套接字连接的地址。 |
public int getPort() | 返回此套接字连接到的远程端口。 |
public int getLocalPort() | 返回此套接字绑定到的本地端口。 |
public SocketAddress getRemoteSocketAddress() | 返回此套接字连接的端点的地址,如果未连接则返回 null。 |
public InputStream getInputStream() throws IOException | 返回此套接字的输入流。 |
public OutputStream getOutputStream() throws IOException | 返回此套接字的输出流。 |
public void close() throws IOException | 关闭此套接字。 |
4、InetAddress 类的方法
这个类表示互联网协议(IP)地址。下面列出了 Socket 编程时比较有用的方法:
方法 | 方法描述 |
---|---|
static InetAddress getByAddress(byte[] addr) | 在给定原始 IP 地址的情况下,返回 InetAddress 对象。 |
static InetAddress getByAddress(String host, byte[] addr) | 根据提供的主机名和 IP 地址创建 InetAddress。 |
static InetAddress getByName(String host) | 在给定主机名的情况下确定主机的 IP 地址。 |
String getHostAddress() | 返回 IP 地址字符串(以文本表现形式)。 |
String getHostName() | 获取此 IP 地址的主机名。 |
static InetAddress getLocalHost() | 返回本地主机。 |
String toString() | 将此 IP 地址转换为 String。 |
- 网络编程
1.1 网络编程基本概念
1、网络
将不同区域的电脑连接到一起,组成局域网、城域网或广域网。把分布在不同地理区域的计算机与专门的外部设备用通信线路互联成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息,共享硬件、软件、数据信息等资源
2、计算机网络
是指将地理位置相同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在==网络操作系统、网络管理软件及通信协议的管理和协调下,实现资源共享和信息传递的计算机系统
3、通信协议
计算机网络中实现通信必须有一些约定即通信协议,对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准
4、通信接口
为了使两个结点之间能进行对话,必须在它们之间建立通信工具(即接口),使彼此之间能进行信息交换
接口包括两部分:
硬件装置:实现节点之间的信息传送
软件装置:规定双方进行通信的约定协议
网络分层
由于结点之间联系很复杂,在指定协议时,把复杂成份分解成一些简单的成份,再将他们复合起来。最常用的复合方式是层次方式,即同层间可以通信、上一层可以调用下一层,而与再下一层不发生关系
TCP/IP是一个协议族,也是按照层次划分,共四层:应用层,传输层,互连网络层,接口层(物理+数据链路层)
OSI网络通信协议模型,是一个参考模型,而TCP/IP协议是事实上的标准。TCP/IP协议参考了OSI模型,但是并没有严格按照OSI规定的七层标准去划分,而只划分了四层,这样会更简单点,当划分太多层时,你很难区分某个协议是属于哪个层次的
网络编程基础知识
一、网络编程三要素
IP地址
端口
协议
二、IP地址与InetAddress类
IP地址分类
InetAddress类
三、端口(Port)与 InetSocketAddress
InetSocketAddress
四、通信协议
传输层协议
TCP/IP协议族
TCP协议
UDP协议
TCP
一、TCP实现聊天
客户端
服务端
二、TCP实现文件上传
客户端
服务端
UDP
一、UDP消息发送
发送端
接收端
二、UDP实现聊天
发送端
接收端
三、多线程UDP聊天程序
发送端
接收端
完成
1.2 网络编程三要素
1、IP地址
要想让网络中的计算机能够互相通信,必须为计算机指定一个标识号,通过这个标识号来指定要接受数据的计算机和识别发送的计算机,而IP地址就是这个标识号,也就是设备的标识。
2、端口
网络的通信,本质上是两个应用程序的通信。每台计算机都有很多的应用程序,那么在网络通信时,如何区别这些应用程序呢?如果说IP地址可以唯一的标识网络中的设备,那么端口号就可以唯一标识设备中的应用程序,也就是应用程序的标识。
3、协议
通过计算机网络可以使多台计算机实现连接,位于同一网络中的计算机进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为计算机网络通信协议。它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。
常见的协议有TCP协议和UDP协议
1.3 IP地址
IP地址:是网络中设备的唯一标识
IP地址分为两大类
IPv4:是给每个连接在网络上的主机分配一个32bit地址。按照TCP/IP规定,IP地址用二进制来表示,每个IP地址的长32bit,也就是4个字节。例如一个采用二进制形式的地址是“11000000 10101000 00000001 01000010”,这么长的地址,处理起来也太费劲了。为了方便使用,IP地址经常被写成十进制的形式,中间使用符号“ . ”分隔不同的字节。于是,上面的IP地址可以表示为“192.168.1.66”。IP地址的这种表示法叫做 “点分十进制法”,这显然比1和0容易记得多
IPv6:由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发紧张。为了扩大地址空间,通过IPv6重新定义地址空间,采用128bit地址长度,每16个字节一组,分成8组十六进制数,这就解决了网络地址资源数量不够的问题
IP地址
是网络中设备的唯一标识
分为两大类
IPv4
IPv6
端口
是设备上应用程序的唯一标识。
端口号:用两个字节表示整数,它的取值范围是0~65535。其中,周知端口: 0~1023之间的端口用于一些知名的网络服务和应用。
注册端口: 普通的应用程序需要使用1024~49151的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。
**动态端口:**49152~65535,之所以称为动态端口,是因为它一般不固定分配某种进程,而是动态分配。
协议
计算机网络中,连接和通信的规则被称为网络通信协议
传输层的两种协议:
UDP协议
TCP协议
Class类常用方法
下面列举了Class类的一些常用方法,这些方法常常会被频繁地使用。
Field[] getFields()
返回一个包含Field对象的数组,存放该类或接口的所有可访问公共属性(含继承的公共属性)。
Field[] getDeclaredFields()
返回一个包含Field对象的数组,存放该类或接口的所有属性(不含继承的属性)。
Field getField(String name)
返回一个指定公共属性名的Field对象。
Method[] getMethods()
返回一个包含Method对象的数组,存放该类或接口的所有可访问公共方法(含继承的公共方法)。
Method[] getDeclaredMethods()
返回一个包含Method对象的数组,存放该类或接口的所有方法(不含继承的方法)。
Constructor[] getConstructors()
返回一个包含Constructor对象的数组,存放该类的所有公共构造方法。
Constructor getConstructor(Class[] args)
返回一个指定参数列表的Constructor对象。
Class[] getInterfaces()
返回一个包含Class对象的数组,存放该类或接口实现的接口。
T newInstance()
使用无参构造方法创建该类的一个新实例。
String getName()
以String的形式返回该类(类、接口、数组类、基本类型或void)的完整名。
Java中的反射及常见API
概述
每个类都有一个 Class 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。
通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。
主要用途
反射最重要的用途就是开发各种通用框架。很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 Bean),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要加载的对象。
常用API
取得一个类的Class对象
1.getClass()
2.Class.forName(全限定名);
获取一个类的父类以及所有实现的接口
1.getSuperclass();
2.getInterfaces();
取得一个类的构造函数
getConstructors();
获取本类以及父类中所有public修饰符修饰的方法
getMethods()
反射优点
可扩展性:应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。
类浏览器和可视化开发环境:一个类浏览器需要可以枚举类的成员。可视化开发环境(如IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。
调试器和测试工具:调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的API定义,以确保一组测试中有较高的代码覆盖率。
反射缺点
尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。
性能开销:反射涉及了动态类型的解析,所以JMM无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。
安全限制:使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。
内部暴露:由于反射允许代码执行一些在正常情况下不被允许的操作
(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
注解
注解的定义
注解通过 @interface 关键字进行定义。
public @interface TestAnnotation {
}
1
它的形式跟接口很类似,不过前面多了一个 @ 符号。上面的代码就创建了一个名字为 TestAnnotaion 的注解。
你可以简单理解为创建了一张名字为 TestAnnotation 的标签。
注解的应用
上面创建了一个注解,那么注解的的使用方法是什么呢。
@TestAnnotation
public class Test {
}
1
创建一个类 Test,然后在类定义的地方加上 @TestAnnotation 就可以用 TestAnnotation 注解这个类了。
你可以简单理解为将 TestAnnotation 这张标签贴到 Test 这个类上面。
不过,要想注解能够正常工作,还需要介绍一下一个新的概念那就是元注解。
元注解
元注解是什么意思呢?
元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。
如果难于理解的话,你可以这样理解。元注解也是一张标签,但是它是一张特殊的标签,它的作用和目的就是给其他普通的标签进行解释说明的。
元标签有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。
@Retention
Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。
它的取值如下:
RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
我们可以这样的方式来加深理解,@Retention 去给一张标签解释的时候,它指定了这张标签张贴的时间。@Retention 相当于给一张标签上面盖了一张时间戳,时间戳指明了标签张贴的时间周期。
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
}
1
上面的代码中,我们指定 TestAnnotation 可以在程序运行周期被获取到,因此它的生命周期非常的长。
@Documented
顾名思义,这个元注解肯定是和文档有关。它的作用是能够将注解中的元素包含到 Javadoc 中去。
@Target
Target 是目标的意思,@Target 指定了注解运用的地方。
你可以这样理解,当一个注解被 @Target 注解时,这个注解就被限定了运用的场景。
类比到标签,原本标签是你想张贴到哪个地方就到哪个地方,但是因为 @Target 的存在,它张贴的地方就非常具体了,比如只能张贴到方法上、类上、方法参数上等等。@Target 有下面的取值
ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
ElementType.CONSTRUCTOR 可以给构造方法进行注解
ElementType.FIELD 可以给属性进行注解
ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
ElementType.METHOD 可以给方法进行注解
ElementType.PACKAGE 可以给一个包进行注解
ElementType.PARAMETER 可以给一个方法内的参数进行注解
ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
@Inherited
Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
说的比较抽象。代码来解释。
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}
@Test
public class A {}
public class B extends A {}
1
注解 Test 被 @Inherited 修饰,之后类 A 被 Test 注解,类 B 继承 A,类 B 也拥有 Test 这个注解。
二、@Override注解
Java 中 @Override 注解是用来指定方法重写的,只能修饰方法并且只能用于方法重写,不能修饰其它的元素。它可以强制一个子类必须重写父类方法或者实现接口的方法。
三、@Deprecated注解
Java 中 @Deprecated 可以用来注解类、接口、成员方法和成员变量等,用于表示某个元素(类、方法等)已过时。当其他程序使用已过时的元素时,编译器将会给出警告。
四、@SuppressWarnings:抑制编译器警告
Java 中的 @SuppressWarnings 注解指示被该注解修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告,且会一直作用于该程序元素的所有子元素。例如,使用 @SuppressWarnings 修饰某个类取消显示某个编译器警告,同时又修饰该类里的某个方法取消显示另一个编译器警告,那么该方法将会同时取消显示这两个编译器警告。
@SuppressWarnings 注解主要用在取消一些编译器产生的警告对代码左侧行列的遮挡,有时候这样会挡住我们断点调试时打的断点。
如果你确认程序中的警告没有问题,可以不用理会。通常情况下,如果程序中使用没有泛型限制的集合将会引起编译器警告,为了避免这种编译器警告,可以使用 @SuppressWarnings 注解消除这些警告。
注解的使用有以下三种:
抑制单类型的警告:@SuppressWarnings(“unchecked”)
抑制多类型的警告:@SuppressWarnings(“unchecked”,“rawtypes”)
抑制所有类型的警告:@SuppressWarnings(“unchecked”)
抑制警告的关键字如下表所示。
关键字 | 用途 |
---|---|
all | 抑制所有警告 |
boxing | 抑制装箱、拆箱操作时候的警告 |
cast | 抑制映射相关的警告 |
dep-ann | 抑制启用注释的警告 |
deprecation | 抑制过期方法警告 |
fallthrough | 抑制在 switch 中缺失 breaks 的警告 |
finally | 抑制 finally 模块没有返回的警告 |
hiding | 抑制相对于隐藏变量的局部变量的警告 |
incomplete-switch | 忽略不完整的 switch 语句 |
nls | 忽略非 nls 格式的字符 |
null | 忽略对 null 的操作 |
rawtypes | 使用 generics 时忽略没有指定相应的类型 |
restriction | 抑制禁止使用劝阻或禁止引用的警告 |
serial | 忽略在 serializable 类中没有声明 serialVersionUID 变量 |
static-access | 抑制不正确的静态访问方式警告 |
synthetic-access | 抑制子类没有按最优方法访问内部类的警告 |
unchecked | 抑制没有进行类型检查操作的警告 |
unqualified-field-access | 抑制没有权限访问的域的警告 |
unused | 抑制没被使用过的代码的警告 |
使用 @SuppressWarnings 注解示例代码如下:
public class Demo07 {
@SuppressWarnings({"deprecation"})
public static void main(String[] args) {
Person p = new Person();
p.setNameAndAge("java", 20);
p.name = "Java教程";
}
}
五、@SafeVarargs注解
在介绍 @SafeVarargs 注解用法之前,先来看看如下代码:
public class HelloWorld {
public static void main(String[] args) {
// 传递可变参数,参数是泛型集合
display(10, 20, 30);
// 传递可变参数,参数是非泛型集合
display("10", 20, 30); // 会有编译警告
}
public static <T> void display(T... array) {
for (T arg : array) {
System.out.println(arg.getClass().getName() + ":" + arg);
}
}
}
代码第 10 行声明了一种可变参数方法 display,display 方法参数个数可以变化,它可以接受不确定数量的相同类型的参数。可以通过在参数类型名后面加入...
的方式来表示这是可变参数。可变参数方法中的参数类型相同,为此声明参数是需要指定泛型。
但是调用可变参数方法时,应该提供相同类型的参数,代码第 4 行调用时没有警告,而代码第 6 行调用时则会发生警告,这个警告是 unchecked(未检查不安全代码),就是因为将非泛型变量赋值给泛型变量所发生的。
六、@FunctionalInterface注解
在学习 Lambda 表达式时,我们提到如果接口中只有一个抽象方法(可以包含多个默认方法或多个 static 方法),那么该接口就是函数式接口。@FunctionalInterface 就是用来指定某个接口必须是函数式接口,所以 @FunInterface 只能修饰接口,不能修饰其它程序元素。
XML基础_Java解析XML
一、XML基础
1.1、XML是什么?
XML(可扩展标记语言)是一种很流行的简单的基于文本的语言来用作应用程序之间的通信模式。它被认为是传输标准装置和存储数据。JAVA提供了极好的支持和丰富的库来解析,修改或查询XML文档。
XML是一种简单的基于文本的语言,它被设计为储存和运输以纯文本格式的数据。它代表着可扩展标记语言。以下是一些XML的显着特征。
XML是一种标记语言。
XML是一种标记语言就像HTML一样。
XML标签不是像HTML那样预定义。
可以定义自己的标签,这就是为什么它被称为可扩展的语言。
XML标签被设计成自描述性的。
XML是W3C推荐用于数据存储和传输。
1.2、XML能干什么?
描述数据、存储数据、传输(交换)数据。
优缺点:
优势
以下是XML提供的优势:
技术无关 - 作为普通文本,XML是技术独立。它可以用于由任何技术进行数据的存储和传输的目的。
人类可读 - XML使用简单的文本格式。它是人类可读和可以理解的。
可扩展性 - 在XML,自定义标签可以创建和很容易使用。
允许验证 - 使用XSD,DTD和XML结构可以很容易地验证。
缺点
下面是使用XML的缺点:
冗余的语法 - 通常XML文件中包含大量的重复计算。
冗余 - 作为一个冗长的语言,XML文件大小增加了传输和存储成本。
1.3、XML与HTML区别
1、目的不一样
2、XML 被设计用来描述数据,其焦点是数据的内容。
3、HTML 被设计用来展示数据,其焦点是数据的外观。
4、HTML可以不关闭标签(即标签可以不成对出现),但XML必须关闭标签(即标签必须成对出现)。
5、HTML中的标签标识文本如何展示,而XML中的标签标识文本是什么含义(什么类型的文本)。
XML文档节点类型
文档(document)
元素(element)
属性(attribute)
文本(PCDATA–parsed character data)
注释(comment)
DOCTYPE :主要验证文档内容的正确性
实体(ENTITIES)
CDATA(character data)
1.4、XML语法
1、声明:<?xml version="1.0" encoding="UTF-8"?>
2、根节点:必须只能有一个根节点
3、标签:标签必须有结束且区分大小写,标签必须顺序嵌套
4、属性:必须引号引起值
5、空格会被保留,HTML空格最多保留一个
6、命名规则:命名必须见名知意
a)名字可包含字母、数字以及其他的字符
b)名字不能以数字或者标点符号开始
c)名字不能以字符“xml”(或者XML、Xml)开始
7、名字不能包含空格
8、 不应在 XML 元素名称中使用 “:” ,这是由于它用于命名空间(namespaces)的保留字。
9、标签优先于属性。
10、XML 命名空间可提供避免元素命名冲突的方法。
11、CDATA:字符数据,<![CDATA[字符数据]]> ,字符数据不进行转义
12、实体:&实体;
<!-- 子节点,随便写,符合规则即可 -->
<user id='Z' number="zpark-001">
<name>zhangsan</name>
<age>23</age>
<gender>nan</gender>
</user>
<user id='L' number="zpark-002">
<name>lisi</name>
<age>24</age>
<gender>nv</gender>
</user>
二、Java XML 教程
1、Java XML 解析器
1.1、什么是XML解析?
解析XML是指将通过XML文档访问数据或修改数据的一个操作或方法。
Java库中提供了两种XML解析器:
1、像文档对象模型(Document Object Model,DOM)解析器这的树型解析器(tree parse),它们将读入的XML文档转换 成树结构。
2、像XML简单API(Simple API for XML,SAX)解析器这样的流机制解析器(streaming parser),它们在读入XML文档时生 成相应的事件。
1.2、XML解析器是什么?
XML解析器提供方法来访问或修改XML文档中的数据。 Java提供了多种选择来解析XML文档。以下是各种类型解析器其通常用于解析XML文档。
Dom解析器 - 解析通过加载该文件的全部内容,并创建其完整分级树中存储的文件。
SAX解析器 - 解析基于事件触发器的文档。不完整(部分)的文件加载到存储器中。
**JDOM解析器 - **解析以类似的方式,以DOM解析器但更简单的方法的文档。
StAX解析器 - 解析以类似的方式,以SAX解析器但在更高效的方式的文档。
**XPath解析器 - **解析基于表达式XML并广泛选择使用XSLT。
**DOM4J解析器 - **Java库来解析XML,XPath和使用Java集合框架XSLT,为DOM,SAX和JAXP的支持。
2、Java DOM解析器
2.1、DOM解析器简介
文档对象模型是万维网联盟(W3C)的官方推荐。它定义了一个接口,使程序能够访问和更新样式,结构和XML文档的内容。支持DOM实现该接口的XML解析器。
何时使用?
在以下几种情况时,应该使用DOM解析器:
1、需要知道很多关于文档的结构
2、需要将文档的部分周围(例如,可能需要某些元素进行排序)
3、需要使用的文件中的信息超过一次
会得到什么?
当使用DOM 解析器解析一个XML文档,会得到一个树形结构,其中包含的所有文档的元素。 DOM提供了多种可用于检查文档的内容和结构的函数。
优势
DOM是用于处理文档结构的通用接口。它的一个设计目标是Java代码编写一个DOM兼容的解析器,运行在任何其他的DOM兼容的解析器不会有变化。
DOM接口
DOM定义了几个Java接口。这里是最常见的接口:
1、节点 - DOM的基本数据类型。
2、元素 - 要处理的对象绝大多数是元素。
3、Attr - 代表元素的属性。
4、文本 - 元素或Attr的实际内容。
5、文档 - 代表整个XML文档。文档对象是通常被称为DOM树。
常见的DOM方法
当正在使用DOM,有经常用到的几种方法:
1、Document.getDocumentElement() - 返回文档的根元素。
2、Node.getFirstChild() - 返回给定节点的第一个子节点。
3、Node.getLastChild() - 返回给定节点的最后一个子节点。
4、Node.getNextSibling() - 这些方法返回一个特定节点的下一个兄弟节点。
5、Node.getPreviousSibling() - 这些方法返回一个特定节点的前一个兄弟节点。
6、Node.getAttribute(attrName) - 对于给定的节点,则返回所请求的名字的属性。
2.2、Java DOM解析器 - 解析XML文档
使用DOM的步骤
以下是在使用DOM解析器解析文档使用的步骤。
1、导入XML相关的软件包。
2、创建DocumentBuilder
3、从文件或流创建一个文档
4、提取根元素
5、检查属性
6、检查子元素
document对象:
要操作XML,先就得有Document对象,把一个XML文件加载进内存的时候,在内存中形成所谓的一种树状结构,我们把这一个结构称之为DOM树.
注意:
我们在Java代码中所做的增/删/改/查操作,都仅仅是操作的是内存中的Document对象,和磁盘中的XML文件没有关系。比如:删除一个联系人信息之后,XML文件中数据依然存在,此时出现内存中的数据和磁盘文件中的数据不同步。所以,对于增删改操作,我们需要做同步操作(把内存中的数据和磁盘的XML文件数据保持一致)。
DOM:在第一次的时候就会把XML文件加载进内存,如果XML文件过大,可能会造成内存的溢出.
DOM:在做增删改查操作的时候比较简单,,但是性能却不高(线性搜索).
3、Java SAX解析器
3.1、Java SAX解析器简介
SAX解析器在解析XML输入数据的各个组成部分时会报告事件,但不会以任何方式存储文档,而是由事件处理器建立相应的数据结构。实际上DOM解析器是在SAX解析器的基础上构建的,它在接收到解析器事件时构建DOM树。
SAX(针对XML的简单API)是基于事件为XML文档的解析器。不像DOM解析器,SAX解析器创建没有解析树。 SAX是一个流接口用于XML的,这意味着使用SAX应用接收事件通知有关XML文档被处理的元素,属性,在按顺序每次开始在文档的顶部,并与所述闭合结束根元素。
1、读取XML文件从上到下,构成一个结构完整的XML文档的标记
2、令牌以相同的顺序进行处理,它们出现在文档中
3、报告应用程序,因为它们所出现解析器遇到标记的特性
4、应用程序提供了必须的解析器注册的“事件”处理程序
5、作为标记标识,在处理程序回调方法相关信息调用
什么时候使用?
应该使用SAX解析器的时候:
1、可以在XML文档从上往下处理以线性方式
2、该文件并不深层次嵌套
3、处理一个非常大的XML文档,DOM树会占用太多的内存。典型DOM的实现使用10字节的存储器以表示XML的一个字节
4、解决的问题涉及的XML文档的一部分
5、数据是可用的,只要它是由解析器看出,这样的SAX可以很好地用于到达流的XML文档
SAX的缺点
1、它是在一个只进入处理随机访问方式XML文档
2、如果需要跟踪的数据分析器已经看到或更改项目的顺序,必须自已编写代码和数据存储
ContentHandler接口
此接口指定SAX解析器用来通知XML文档,已经看到部件应用程序的回调方法。
方法 | 方法描述 |
---|---|
void startDocument() | 调用在一个文件的开头。 |
void endDocument() | 调用在一个文件的末尾。 |
void startElement(String uri, String localName, String qName, Attributes atts) | 调用在一个元素的开头 |
void endElement(String uri, String localName,String qName) | 调用在一个元件的末端。 |
void characters(char[] ch, int start, int length) | 字符数据出现时调用。 |
void ignorableWhitespace( char[] ch, int start, int length) | 当DTD是当前和忽略空白遇到时调用。 |
void processingInstruction(String target, String data) | 当处理指令的认可时调用。 |
void setDocumentLocator(Locator locator)) | 提供可用于识别文档中的位置的定位器。 |
void skippedEntity(String name) | 一个尚未解决实体遇到时调用。 |
void startPrefixMapping(String prefix, String uri) | 当一个新的命名空间的映射定义调用。 |
void endPrefixMapping(String prefix) | 当一个命名空间定义结束其范围时调用。 |
属性接口
这种接口指定用于处理连接到一个元素的属性的方法。
int getLength() - 返回属性的数目。
String getQName(int index)
String getValue(int index)
String getValue(String qname)
3.2、Java SAX解析器 - 解析XML文档
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Of71b6te-1669955857386)(images.assets/image-20210815153319390.png)]
在使用SAX解析器时,需要一个处理器来为各种解析器事件定义事件动作。DefaultHandler接口定义了若干个在解析文档时解析器会调用的回调方法。下面是最重要的几个方法:
1、startElement和endElement在每当遇到起始或终止标签时调用。
2、characters在每当遇到字符数据时调用。
3、startDocument和endDocument分别在文档开始和结束时各调用一次。
例如,在解析以下片段时:
<person>
<name type="string">韩信</name>
<age>25</age>
</person>
解析器会产生以下回调:
1)startElement,元素名:person
2)startElement,元素名:name ,属性:type=“string”
3)characters,内容:韩信
4)endElement,元素名:name
5)startElement,元素名:age
6)characters,内容:25
7)endElement,元素名:age
8)endElement,元素名:person
处理器必须覆盖这些方法,让它们执行在解析文件时我们想让它们执行的动作。下面通过一个简单的demo体会SAX解析XML的过程。
准备xml
首先准备person.xml,内容如下
<?xml version="1.0" encoding="UTF-8" ?> 韩信 25 李白 23 **解析代码** 1、获取解析工厂 2、从解析工厂获取解析器 3、得到解读器 4、设置内容处理器 5、读取xml的文档内容 一、什么是WEBserverWebserver能够解析HTTP协议。当Webserver接收到一个HTTP请求,会返回一个HTTP响应,比如送回一个HTML页面。为了处理一个请求Webserver能够响应一个静态页面或图片,进行页面跳转或者把动态响应的产生托付给一些其他的程序比如CGI脚本,JSP脚本,servlets,ASP脚本,server端JavaScript,或者一些其他的server端技术。不管它们(译者注:脚本)的目的怎样,这些server端的程序通常产生一个HTML的响应来让浏览器能够浏览。
二。经常使用的WEBserver有哪些?
在UNIX和LINUX平台下使用最广泛的免费HTTPserver是W3C、NCSA和APACHEserver,而Windows平台NT/2000/2003使用IIS的WEBserver。在选择使用WEBserver应考虑的本身特性因素有:性能、安全性、日志和统计、虚拟主机、代理server、缓冲服务和集成应用程序等,以下介绍几种经常使用的WEBserver。
Microsoft IIS
Microsoft的Webserver产品为Internet Information Server (IIS), IIS 是同意在公共Intranet或Internet上公布信息的Webserver。IIS是眼下最流行的Webserver产品之中的一个,非常多著名的站点都是建立在IIS的平台上。IIS提供了一个图形界面的管理工具,称为 Internet服务管理器,可用于监视配置和控制Internet服务。
IIS是一种Web服务组件,当中包含Webserver、FTPserver、NNTPserver和SMTPserver,分别用于网页浏览、文件传输、新闻服务和邮件发送等方面,它使得在网络(包含互联网和局域网)上公布信息成了一件非常easy的事。它提供ISAPI(Intranet Server API)作为扩展Webserver功能的编程接口;同一时候,它还提供一个Internet数据库连接器,能够实现对数据库的查询和更新。
IBM WebSphere
WebSphere Application Server 是 一 种功能完好、开放的Web应用程序server,是IBM电子商务计划的核心部分,它是基于 Java 的应用环境,用于建立、部署和管理 Internet 和 Intranet Web 应用程序。 这一整套产品进行了扩展,以适应 Web 应用程序server的须要,范围从简单到高级直到企业级。
WebSphere 针对以 Web 为中心的开发者,他们都是在基本 HTTPserver和 CGI 编程技术上成长起来的。IBM 将提供 WebSphere 产品系列,通过提供综合资源、可反复使用的组件、功能强大并易于使用的工具、以及支持 HTTP 和 IIOP 通信的可伸缩执行时环境,来帮助这些用户从简单的 Web 应用程序转移到电子商务世界。
BEA WebLogic
BEA WebLogic Server 是一种多功能、基于标准的web应用server,为企业构建自己的应用提供了坚实的基础。各种应用开发、部署全部关键性的任务,不管是集成各种系统和数据库,还是提交服务、跨 Internet 协作,起始点都是 BEA WebLogic Server。因为 它具有全面的功能、对开放标准的遵从性、多层架构、支持基于组件的开发,基于 Internet 的企业都选择它来开发、部署最佳的应用。
BEA WebLogic Server 在使应用server成为企业应用架构的基础方面继续处于率先地位。BEA WebLogic Server 为构建集成化的企业级应用提供了稳固的基础,它们以 Internet 的容量和速度,在连网的企业之间共享信息、提交服务,实现协作自己主动化。
APACHE
apache仍然是世界上用的最多的Webserver,市场占有率达60%左右。它源于NCSAhttpdserver,当NCSA WWWserver项目停止后,那些使用NCSA WWWserver的人们開始交换用于此server的补丁,这也是apache名称的由来(pache 补丁)。世界上非常多著名的站点都是Apache的产物,它的成功之处主要在于它的源码开放、有一支开放的开发队伍、支持跨平台的应用(能够执行在差点儿全部的Unix、Windows、Linux系统平台上)以及它的可移植性等方面。
Tomcat
Tomcat是一个开放源码、执行servlet和JSP Web应用软件的基于Java的Web应用软件容器。Tomcat Server是依据servlet和JSP规范进行执行的,因此我们就能够说Tomcat Server也实行了Apache-Jakarta规范且比绝大多数商业应用软件server要好。
Tomcat是Java Servlet 2.2和JavaServer Pages 1.1技术的标准实现,是基于Apache许可证下开发的自由软件。Tomcat是全然重写的Servlet API 2.2和JSP 1.1兼容的Servlet/JSP容器。Tomcat使用了JServ的一些代码,特别是Apache服务适配器。随着Catalina Servlet引擎的出现,Tomcat第四版号的性能得到提升,使得它成为一个值得考虑的Servlet/JSP容器,因此眼下很多WEBserver都是採用Tomcat。
眼下,很多大型Web应用一般将Apache和Tomcat结合使用,Apache负责接收用户的HTTP请求,假设请求是Servlet、Jsp,则把请求转发给Tomcat处理,并将处理结果封装响应给用户。
Java 的静态工厂模式和抽象工厂模式
1、静态工厂模式
1.1、优缺点
1.2、实现案例
1.2.1、抽象类
1.2.2、实现类
1.2.3、静态工厂类
1.2.4、测试类
2、抽象工厂模式
2.1、适用场景
2.2、案例介绍
2.2.1、抽象类
2.2.2、实现类
2.2.3、工厂类
2.2.4、测试类
1、静态工厂模式
静态工厂模式,在平常开发中是比较常见的。
1.1、优缺点
优点:
将创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建,明确了职责。
把初始化实例时的工作放到工厂里进行,使代码更容易维护。 更符合面向对象的原则,面向接口编程,而不是面向实现编程。
缺点:
由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
要新增产品类的时候,就要修改工厂类的代码,违反了开放封闭原则。即:对扩展的开放,对修改的关闭。
简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构.
Java 普通工厂模式
1、基本概念
官方术语:先创建一个工厂类,对实现了同一个接口的不同实现类进行实例创建。
大白话:说白了就是你想要哪个类跟工厂说就行,工厂把你想要的类创建好,然后你拿过来直接就能用。
种类:工厂模式又可以分为四种:普通工厂模式、工厂方法模式、静态工厂模式、抽象工厂模式。
前三种工厂模式是违背开闭原则的,因为你每次新增一个业务,增加业务类的同时,工厂类里面也需要进行修改。
抽象工厂类没有违背开闭原则,因为每个业务都有自己对应的工厂,每次新增一个业务,也会去新增一个对应的工厂,大家互不影响。
开闭原则:扩展开放,修改关闭。说白了就是你要新增一个业务的时候不要影响到之前的,不要我每次新增一个业务都要去原来的里面去修改。
优点:在需要大量创建对象的前提下,用工厂模式扩展性好,可维护性强。
缺点:代码稍微复杂,可读性稍微有点儿差。