JAVAOO
第一章:java语法基础
Java标识符命名规则:
1标识符由字母、下划线“_”、美元符“$”或数字组成。
2标识符应以字母、下划线、美元符开头。
3Java标识符大小写敏感,长度无限制。
4“见名知意”
Java数据类型:4类8种
数值型:byte、short、int、long、float、double
字符型:char
布尔型:boolean
引用类型:string
基本数据类型:
容量小的类型自动转换为容量大的数据类型;
数据类型按容量大小为:byte、short、int、long、float、double
第二章:程序流程控制
组合布尔逻辑:
和:两个表达式均为true,组合表达式为真
或:两个表达式有一个为true,组合表达式为真
异或:两个表达式都为false,组合表达式为真
非:非真为假,非假为真
break:
用break语句可以跳出switch语句体,程序继续执行switch语句体下面的程序。
在循环结构中,也可以用break语句跳出本层循环体,从而提前结束层循环。
Continue:
continue的作用是结束本次循环(即跳过本次循环中余下尚未执行的语句),接着再一次进行循环条件的判定。
第三章:类、对象、包
结构化和oo的区别:
在结构化编程中,程序围绕着要解决的任务来设计。
面向对象编程中,程序围绕着问题域中的对象来设计。
类:具有相同属性和行为的一组对象的集合。类是对象的抽象,对象是类的实例。类是一个模板,对象则是这个模板的具体实现
属性:即成员变量(全局变量),定义在类里,并且在方法外。 属性的访问通过对象.(的)方式访问。
属性的特点:包含在对象之中,它的值发生变化,不会影响其他对象。
常量:作用1、见名知意 2、方便修改
常量在声明的同时必须进行初始化 静态类成员: static修饰的全局变量叫静态变量,也叫类变量。
类变量的值为所有对象所共享
静态变量的特点:1、唯一性 2、对象之外存放2、加载时机在对象之前
静态变量是在类加载时产生,所以产生的时机在对象之前。静态方法产生于对象之前,所以不能直接访问非静态变量。
变量:命了名的内存空间,并且它的值可变化
垃圾回收站收集没有被变量指向的对象,回收堆内存中的对象。
包(package)的机制:
1提供了一个组织类的机制;
2为包中的类提供了一个命名空间
第四章:对象的行为
Java里面只有按值传递:当实际参数传递给形式参数时,实际参数的数据被复制给形式参数。 不管传递给方法的实际参数是什么类型,相关的形式参数都会得到该数据的一份拷贝,这就是按值调用的工作原理。
方法重载:当一个类中有两个到多个同名但是有不同的参数列表的方法时,就是方法重载。
构造方法特点:1、构造方法的名称必须要与类名相同
2、构造方法没有任何返回类型
构造方法的作用: 1、为对象分配内存
2、创建并初始化变量
3、初始化成员变量
4、返回引用
如果一个类里没有构造方法,系统会分配一个默认的无参构造方法。如果一个类有一个有参的构造方法,默认无参的构造方法就不存在。
抽象:我们在认识事物的时候只关注其中一部分而忽略次要部分。
面向对象:一种认识事物的方式,注重对事物整体的认知,更符合人类自然的思维习惯。
类:具有相同属性和行为的一组对象的集合。类是对象的抽象,对象是类的实例。类是一个模板,对象则是这个模板的具体实现。
面向对象的主要特征:继承、封装、多态。
继承的特点:具有层次结构,子类继承了父类的属性和方法。提高代码的重用性。
第五章:数组
创建数组:
1、声明一个数组变量
数据类型[] 数组变量名; 或 数据类型 数组变量名[] Eg: int[] sums;或int sums[]
2、使用new关键字初始化数组,并指定数组长度:
数组变量 = new 数组类型[数组长度] eg:sums = new int[20];
访问数组:
数组中的元素通过用该数组的变量名(引用)加上一个用于描述我们要访问数组哪一个元素的整型索引值来访问。
数组在内存中是地址连续的一串空间。所以一旦分配好数组的长度就不能修改了。Java中的数组是对象。这样做的好处之一为:Java中的每个数组都有一个length属性来代表数组的大小,可以极大地减少数组访问越界的可能性。
数组特点
数组名称引用整个数组所占的整片内存空间
每个空间所包含的值称为数组元素
通过下标(索引)引用数组中的每一个元素,从下标从0开始
数组的长度length(public 修饰符) 注意和String的length()方法区别
int array[]=new int[5];
System.out.println(array.length);//求出数组长度
结果是5。但最大下标是4,因为数组下标从零开始,最大下标等于数组长度减1
对于指定了长度的数组要初始化的化需要通过下标一个个的赋值很麻烦
int array[] =new int[5];
array[0]=9;array[1]=19;……
因此我们可以在声明数组的同时就初始化而不需要指定长度,长度默认是初始化元素的个数。元素之间使用逗号隔开
String[] array=new String[]{“41”,“62”,“35”,“54”};//数组长度为4
String[] array={“41”,“62”,“35”,“54”};
那么,注意这2种方式的区别:
在内存行为里的区别(指定长度的方式虽然在内存里开辟空间但是里面还没有放东西而另一种是开辟的同时往里面放入了值)
数组一旦创建,那么数组的长度就固定下来了,指定长度的方式可能会造成内存空间不足或空间浪费
数组复制:
1、创建一个新数组,然后使用for循环挨个拷贝
2、System类中的静态方法arraycopy()。
JavaDoc简介:
@author:表示源代码的作者名称。
@version:描述类的软件版本。
@see:创建一个“参见”XX条目的链接,如类、方法、变量等。
@param:用于描述方法的形式参数。
@return:用于描述方法的返回值。
@exception:被方法所用,列出抛出的异常
第六章:继承
一个新类可以从现有的类中派生,这个过程称为类继承。现有类称为新类的父类(基类),新类称为现有类的子类(派生类)
继承可提高代码的重用性,使用extends关键字来实现。父类的方法和属性部分被子类的对象继承
如果一个类被申明成final修饰符,那么这个类就不能被继承了。
private修饰符的属性只能由本类调用,缺省成员可以由本类和同包内的其他类调用,protected成员可以由本类、同包内的其他类,以及不同包的子类访问。而public修饰符成员可以被所有的类访问。
父类的构造方法不能被它的子类继承.
子类对象构建时,会先去调用父类的构造方法。(没有父亲,哪来的儿子),并且子类需要通过父类构造方法的调用来完成从父类继承下来的属性初始化。
可以使用super()显式地调用父类的构造方法,如果父类构造方法有参数,也可以在括号中跟参数。
构造方法一旦有显示的申明,则隐式的构造方法就不存在了。这样,如果父类有显示、带参数的构造方法,则子类必须使用super(参数)显示的调用父类的构造方法
如果子类没有定义构造方法,系统会默认地添加一个无参的构造方法,并在第一句自动调用父类不带参数的构造方法super()。
在JAVA中,类的继承是单根继承,就是说一个类如果继承了另一个类之后,就不能再继承其他类了。
多态概念:允许一个父类变量引用子类,允许一个接口类型变量引用实现类对象。反过来却不行。具体表现为重载和重写。
使用instanceof的用于判断一个对象的实际类型,这个运算符用于判断一个对象是否是指定的类型。返回一个布尔值,是true表示匹配该类型,是false表示不匹配该类型。
子类重写父类的方法,访问修饰符必须和父类的访问修饰符相同,或比父类访问修饰符更大。
子类重写父类的方法后,在调用时优先调用子类的方法。子类没有该方法时,才去调用父类的方法。
方法重写:
子类的方法的返回值的类型、方法名和形式参数列表,必须和父类中的是相同的。 如果子类中的方法与父类中的方法同名,但是子类的方法改变了形式参数列表,那么这是方法重载,而不是方法重写。不要混淆这两个概念,二者的用法是完全不同的。
访问修饰符必须不小于父类中的访问修饰符。例如,如果父类的方法是public,那么子类的必须是public。如果父类的方法是protected,那么子类必须是protected或public(public比protected访问权更大)。
子类中重写的异常不能抛出比父类更多的异常
方法重写与方法重载的区别:
方法的重写:子类中的方法与父类中的方法相同(方法名称、参数列表、返回类型)
方法重载:一个类中的方法与另一个方法相同,参数列表不同
重写体现的是父类与子类方法之间的关系;
重载体现的是一个类的内部方法之间的关系。
第七章:类的高级概念
访问修饰符:
1、公开访问级别:使用public关键字修饰。用public关键字修饰的成员对外公开,即公开成员可以被任何其它对象访问。
2、受保护访问级别:使用protected关键字修饰。受保护的成员可以被同一包中的类所访问,还可以被类的子类所访问,不管子类是在哪个包中。
3、默认访问级别:没有访问修饰符。默认访问级别的成员可以被同一包中的其它类所访问。
4、私有访问级别:使用private关键字修饰。它是四种访问修饰符中级别最低的。私有成员只有类本身可以访问,不对外公开。
封装:一个类的组件(数据成员或方法)被声明为private。生成getXxxx()----访问器 和setXxxx()----修改器。
封装的好处:
类的成员变量可以成为只读或者只写的。例如,SalesPerson类中的成员变量id是只读的,在SalesPerson对象实例化后,id成员变量就无法改变了。
类可以对存储在其成员变量中的内容有一个整体的控制。例如,SalesPerson类中的成员变量commissionRate的值只能在0.0和0.20之间。
类的用户不需要知道类是如何存储数据的。类可以改变一个成员变量的数据类型,而类的用户不需要改变其代码。
静态类成员:
static修饰的全局变量叫静态变量,也叫类变量。类变量的值为所有对象所共享
static修饰的方法叫静态方法,即类方法。类方法只能使用静态变量,而不能使用非静态的全局变量
用类成员(类变量和类方法)不需要创建实例,可以通过:className. variableName的形式直接引用
注意:静态方法不能访问非静态成员 非静态方法可以访问静态成员
静态初始化块: 一个类中由JVM的类加载器加载一次
成员内部类:
class Outer {
class Inner {
}
} 只有创建了Outer类的实例对象后,才能使用Inner类的对象
静态内部类:
class Outer {
static class Inner {
}
} 可以不创建Outer类的对象,而直接引用Inner类。
局部内部类:局部内部类是在类的方法内部定义的类。局部内部类只能在方法内部中使用。一旦方法执行完毕,局部内部类就会从内存中被清除。
匿名内部类:匿名内部类是一种特殊的内部类,这种类没有名字。匿名内部类的定义与对象的创建合并在一起,整个动作看起来像产生对象似的。匿名内部类是为唯一对象而定义的类。
第八章:多态与抽象
多态的概念:相同的行为,不同的实现。
多态的分类:静态多态(使用方法重载实现)、动态多态(使用动态绑定和方法重写实现)。
动态多态的技术基础:
向上转型技术:一个父类的引用变量可以指向不同的子类对象,或者说一个子对象可以被当作一个父类类型。
instanceof关键字:instanceof关键字用于判断运行时对象的真正类型。
动态绑定技术:运行时根据父类引用变量所指对象的实际类型执行相应的子类方法,从而实现多态性。
向上转型和向下转型:
当从低精度数据类型向高精度数据类型转换时执行自动转换,这种类型转换技术称为向上转型。
当从高精度数据类型向低精度数据类型转换时执行自动转换,这种类型转换技术称为向下转型。
多态总结:
1使用父类类型的引用指向子类的对象。
2该引用只能调用父类中定义的方法,不能调用子类中独有的方法。
3如果子类中重写了父类中的一个方法,那么在调用该方法的时候,将会调用子类中的这个方法。
4在多态中,子类可以调用父类中的所有方法
抽象的使用:我们发现父类没有必要进行实例化的时候,可以将该类定义为抽象类(abstract)。
抽象类:将一个方法声明为抽象方法有两个结果:
1、类也必须声明为抽象类。如果一个类包含了抽象方法,那么该类也必须是抽象的。
2、任何子类必须重写抽象方法,除非子类本身也是抽象的。
第九章:接口
接口是抽象类的特例,在抽象类中的方法可以实现,也可以用抽象方法定义,但在接口中就只能是定义,不能有实现。
定义接口,使用关键字interface
接口里的方法都是public公有方法,即使不写,默认也是pubic的。其他的修饰符都无效。
接口定义出来,是要被类所实现的。定义一个类实现接口,就得实现接口里定义的所有的方法。使用关键字implements
接口是Java编程一项重要的技术,通过它可以实现多态,同时它也弥补了Java单一继承的不足。之前说到类是单根继承,一个类继承了另一个类之后,就不能继承其他类了。但现实生活中很多事物都同时拥有很多类的特性。为了解决这个问题,在JAVA中使用接口来进行描述这些事物不同的行为,在JAVA中一个类可以实现多个接口。这样的话,这个类就得实现每个接口所定义的方法。
接口中可以定义变量,但接口中的变量是公共的静态的常量。(public static final)
接口可以继承接口,继承之后会拥有父接口所定义的方法。集合框架JCF就是一个例子。Conllection是父接口,List和Set是子接口。接口可以是多继承。
一个类要实现一个接口,那么就得实现这个接口和这个接口父接口所定义的所有方法。
当一个类实现接口时,可以使用这个接口的变量去指向这个类的对象。
接口是在调用和实现中间加了一层。将调用和实现分离开来,增加程序的灵活性、扩展性和可维护性。
抽象类和接口区别:
1抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
2抽象类要被子类继承,接口要被类实现。
3接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
4接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
5抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
6抽象方法只能申明,不能实现。abstract void abc();不能写成abstract void abc(){}。
7抽象类里可以没有抽象方法
8如果一个类里有抽象方法,那么这个类只能是抽象类
9抽象方法要被实现,所以不能是静态的,也不能是私有的。
10接口可继承接口,并可多继承接口
第十章:异常处理
什么是异常:异常是程序正常执行过程中出现的不正常的情况。
异常捕获的顺序:当一个异常发生或抛出。那么正常的程序执行序列将被终止,程序将对异常进行处理,我们称之为抛出异常被捕获。JAVA中通过使用try-catch语句把可能抛出异常的语句包起来。
所有异常类的超类是Throwable。在Throwable下有两个子类,一个是Error,另一个是Exception。Error是错误,程序员无法控制,Exception是异常,程序员可以通过异常捕获加以控制。例如:电脑运行中中毒了,我们可以通过杀毒软件来处理,处理完了以后可以继续使用,这叫异常,我们可以控制。但电脑出现了蓝屏或死机的时候,我们就不能进行任何操作了。这就是错误,我们无法控制。
怎样捕获一个异常:
a、 把正常运行的代码放在try块里。
b、 如果程序中出现了异常就把异常抛出在catch中,如果catch中能捕获抛出的异常。那么就会进行处理。处理的代码写在catch块中。
try程序块里面的语句是按顺序执行的语句
当try程序块里面的语句抛出一个异常的时候,程序的控制转向了相匹配的catch程序块,catch程序块里面的语句被执行。
当异常发生后,程序执行将忽略try程序块中剩余的语句,继续执行程序块后面的语句。
如果在try程序块中没有抛出异常,那么catch块将被忽略。程序将继续执行try-catch下面的语句
声明异常:throws关键字
抛出异常:throw关键字
Finally关键字:关键字finally用于在try块后创建一个代码块。Finally代码块总是会执行,不管异常是否发生。我们可以使用finally块来执行清理类型的语句,而不管被保护的代码中发生了什么。即使try程序块内部有一个return语句,finally语句块也会执行。
重写父类的方法不能抛出比父类方法更多的异常。
用户自定义异常:
1所有的异常必须是Throwable的子类
2如果我们想编写一个可以自动被异常处理或声明规则强制的检查异常,就需要继承Exception类;
3如果想编写一个运行时异常,就需要继承RuntimeExceptin类。
异常捕获与多态性:
虽然一个try块后可以跟随多个catch块,都是catch块不能简单的以任意顺序列出。当异常发生时,catch块依照它们排列的顺序被依次检查。由于多态性(多态性的is_a关系)的存在,一个catch块有可能不会被检查到。捕获异常时:子类在前,父类在后。
异常处理及声明规则:这个规则指出一个检查异常要么被处理,要么被声明。处理异常是指异常的捕获,而声明异常是指一个方法在方法签名是使用thrws关键字,但是声明的异常在该方法中不会被处理。异常处理和声明规则不适合用于运行时异常。
第十一章:常用类
系统相关类:System类、Runtime类
System类:是一个final类,该类的所有属性和方法都是静态的。System类的属性in、out、err分别对应标准输入、标准输出和错误输出流。System类常用于记录程序执行的时间、复制数组、确定当前的系统属性和获取系统环境变量等工作。
Runtime类:代表java程序的运行时环境。
字符串相关类:String类、StringBuffer类、StringBuilder类
String类:String类包含了一个不可变的字符串。一旦一个String对象被创建,包含这个对象中的内容是不可变的,直至这个对象被销毁。创建String对象的方法有很多种,最常见的有两种:1使用String对象的构造器显示创建eg:String str=new String(“hello”) 2通过用字符串常量直接给String类型变量赋值隐式创建eg:String str=“hello”。
StringBuffer类:StringBuffer对象则代表一个内容可变的字符串。StringBuffer类的append()方法,使用该方法进行字符串的拼接,将比String更加节省内存。
StringBuilder类:StringBuilder类也代表字符串对象,和StringBuffer在很多个方面是相同的,不同的地方是StringBuffer方法是线程安全的,而StringBuilder不是。所以,StringBuilder比StringBuffer性能略高。
日期相关类:Date类、DateFormat抽象类与SimpleDateFormat类、Calendar抽象类与GrregorianCalendar类
Date类:Long getTime()返回自1970年1月1日00:00:00GMT以来此Date对象表示的毫秒数。
数学运算相关类:1、Math类(其中所有方法都是静态的,可以直接通过Math.方法名()进行调用)eg:Math.ceil()___向上取整 Math.round()___四舍五入 Math.floor()__向下取整
2、随机数:1通过System.currentTimeMillis( )来获取一个当前时间毫秒数的long型数字,我们可以把这个数字当作随机数;2通过Math类的静态方法random()返回一个0到1之间的double值。我们可以将这个值乘以一定的数,比如100,那么它就是100以内的随机数;3通过Random类来产生一个随机数。
包装类与自动拆箱装箱:
基本数据类型 |
包装类 |
boolean |
Boolean |
byte |
Byte |
short |
Short |
int |
Integer |
long |
Long |
char |
Character |
flaot |
Float |
double |
Double |
在使用包装类时必须注意:1所有的包装类都是final类型,不能创建它们的子类;2包装类是不可变类。一旦创建了一个包装类对象,那么它们所包含的基本类型数据就不能改变;3自动装箱/拆箱虽然提供了基本数据类型和包装类之间自动转换的机制,但是自动转换会带来一些额外的系统开销。
正则表达式:是根据一组字符串中每个字符串所共有的特征,使用特定的符号来描述该组字符串的一种方法。正则表达式可用于搜索、编辑或操纵文本和数据。Java中提供了Pattern类和Matcher类使用正则表达式。
第十二章:反射、类加载与垃圾回收
类的加载机制:1.类加载 2.连接 3.初始化
类加载:java类文件通过类加载器加载到内存中。类加载器包括根类加载器、扩展类加载器、系统类加载器。此外,Java API还提供了一个ClassLoader抽象类,开发者可以通过继承它的基类来创建自定义的类加载器。
用反射生成并操作对象:
Class对象可以获得该类里的成分包括方法(由Method对象表示),构造器(由Constructor对象表示),Field(由Field对象表示),这三个类都定义在java.lang.reflect包下,并实现了java.lang.reflect.Member接口,程序可以通过Method对象来执行对应的方法,通过Constructor对象来调用对应的构造器创建对象,能通过Field对象直接访问并修改对象的属性值。
创建对象:
通过反射来生成对象有如下两种方式:
1).使用Class对象的newInstance()方法来创建该Class对象对应类的实例,这种方式要求该Class对象的对应类有默认构造器,而执行newInstance()方法实际上是利用默认构造器来创建该类的实例。
2).先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。通过这种方式可以选择使用某个类的指定构造器来创建实例。
通过第一种方式来创建对象是比较常见的情形,因为在很多Java EE框架中都需要根据配置文件信息来创建Java对象,从配置文件兑取的只是某个类的字符串类名,程序就需要根据字符串来创建对应的实例,就必须使用反射。
如果我们不想利用默认构造器来构建Java对象,而想利用指定的构造器来创建Java对象,则需要利用Constructor对象了,每个Constructor对应一个构造器。为了利用指定构造器来创建Java对象需要如下三步:
1).获取该类的Class对象
2).利用Class对象的getConstructor()方法来获取指定构造器。
3).调用Constructor的newInstance()方法来创建Java对象。
调用方法:
当获得某个类对应的Class对象后,就可以通过该Class对象的getMethods()方法或者getMethod()方法来获取全部方法或指定方法——这两个方法的返回值是Method对象组,或者Method对象。
每个Method对象对应一个方法,获得Method对象后,程序就可以通过该Method来调用对应方法,在Method里包含一个invoke()方法,该方法的签名如下:
1).Object invoke(Object obj,Object args):该方法中的obj是执行该方法的主调,后面的args是执行该方法的实参。
访问属性值:
通过Class对象的getFields()或getField()方法可以获取该类所包括的全部Field(属性)或指定Field.Field提供了如下两组方法来访问属性:1).getXxx(Object obj):获取obj对象该Field的属性值。此处的Xxx对应8个基本类型,如果该属性的类型是引用类型则取消get后面的Xxx。
2).setXxx(Object obj,Xxx val):将obj对象的该Field设置为val值。此处的Xxx对应8个基本类型,如果该属性的类型是引用类型则取消set后面的Xxx。
java类的加载顺序:
1.按代码顺序递归加载静态成员/代码块,先父类再本类.
2.按代码顺序递归加载非静态成员/代码块,先父类再本类.
3.按代码顺序递归调用构造函数,先父类再本来.
java类的加载事项:1.调用静态成员时,加载静态成员所在的类及父类.
2.第一次new对象时加载(第二次new不再加载)
3.加载子类会先加载父类.有static和final修饰的成员不会被加载,当成常量使用.
垃圾回收机制:
1通过将引用赋值为null,或者将引用赋值为其他对象,或使引用脱离范围,可以让对象成为不可获得。当一个对象成为不可获得时,就可能被垃圾回收器回收。
2System.gc()或者Runtime.gc()方法是程序员与JVM的垃圾回收器进行通讯的唯一机制,但是执行gc()方法,并不能保证垃圾回收器立即执行垃圾回收,只能促使垃圾回收器尽快回收无用的对象。
3对象的finallize()方法可以帮助我们完成一项释放对象所占用的资源等收尾工作。但是垃圾回收器是否会执行finallize()方法、以及何时执行该方法,都是不确定的。
第十三章:多线程编程
线程概述:
进程:一个正在运行的程序通常称为一个进程,每个进程都有自己独立的一块内存空间,每个进程的内部数据和状态都是完全独立的。
线程:一个程序在同一时间内可以运行多个任务,每一个任务称为一个线程,线程可以共享同一块内存和系统资源。
Thread类:在java中,虚拟CPU嵌入到Thread类中;构造一个线程时,通过构造器参数传递执行代码和数据。一个线程可以执行相同或者不同于其他线程的代码,也可以访问相同或不同于其他线程的数据。
Runnable类:Runnable接口中为所有需要线程执行的任务定义了一个公共规范。
Object类: Object类中包含了一下多线程相关的方法。Eg:void wait()__导致当前线程在该对象上无限期等待,直到其它线程调用相同对象的notify()方法或notifyAll()方法通知它恢复为止。 void notify() ____唤醒正在该对象上等待的一个线程。当前线程必须拥有对象的锁以调用该方法。
线程的创建:
1编写一个继承Thread类的类,然后在类中重写Thread类的run()方法;
2编写一个类实现Runnable接口,然后将该类的实例与java.lang.Thread对象联系在一起。
通过Thread类来创建线程:
1创建一个继承Thread类的类;
2在创建的Thread子类中重写run()方法,在该方法中写入想要线程运行的代码;
3创建Thread子类的实例;
4通过调用该实例上的start()方法,开始运行线程。
注:要启动线程必须使用start()方法,再由该方法去调用run()方法。如果直接调用Thread类中的run()方法,虽然程序也可以运行,但是我们只是调用了类中的方法,而不是启动了线程。
通过Runnable接口来创建线程:
1创建一个类实现Runnable接口,用于代表我们需要线程完成的任务;
2在Runnable指定的run()方法内,放入我们想要在线程走执行的代码;
3创建一个Runnable类的实例;
4创建一个Thread对象,将Runnable的实例作为构造器参数传入进去。要实际创建一个线程,需要将Runnable实例传递到Thread类的构造器中。Eg:Thread(Runnable thrdObj, String thrdName),这里thrdObj是实现Runnable接口的类的实例,thrdName是指定线程的名称;
5通过调用Thread类的实例的start()方法,开始执行线程。调用start()方法将导致调用该线程关联的Runnable实例的run()方法。
两种创建方式的比较:
1使用Runnable接口可以将虚拟CPU与线程要完成的任务有效分离,较好的体现了面向对象设计的基本原则;
2可以避免java单继承的局限。在实际开发中,如果已经继承了某个类的子类要放入多线程中,由于java中一个类不能拥有多个父类,所以就不能使用继承Thread类的方法,只能采用实现Runnable接口的方式。
线程的优先级:
线程的运行顺序是由优先级决定的。Java的线程优先级是一个介于1(MIN_PRIORITY)到10(MAX_PRIORITY)之间的整数值。优先级数值越大,表示优先权越高。Main线程的都是默认值5。
Yield()方法:
暂停当前正在执行的线程对象,并执行其他线程。和sleep()不同的是yield()方法会让线程由Running状态直接回到Runnable状态,而不会阻塞(Blocked)一段指定的时间。因此如果调用yield()方法的线程优先级太高的话,该线程可能会立刻由Runnable状态变为Running状态。
Join()方法:
该方法会让当前正在运行的线程暂停运行,并等待调用join()方法的线程运行完成后,才继续运行。
isAlive()方法:
线程会在运行start()方法进入它的生命周期,而在run()方法运行完成后结束该线程的运行。因此只要是线程的run()方法未运行完毕,该线程都在他的生命周期中。Thread类中提供了isAlive()方法判断运行是否仍在其生命周期中。
线程同步:
当两个以上的线程需要访问共享资源时,我们必须确定在同一时间只有一个线程能够存取共享资源,而运行这个目标的过程就称为同步。例如,一个线程可能尝试从一个文件中读取数据,而另一个线程则尝试在同一文件中修改数据。在此情况下,数据可能会变得不一致。为了确保在任何时间点一个共享的资源只被一个线程使用,使用了“同步”。使用同步关键字synchronized来进行标识
同步方法:
synchronized void display(int num) {
System.out.print(""+num);
try {
Thread.sleep(1000);
}
catch(InterruptedException e) {
System.out.println("中断");
}
System.out.println(" 完成");} 在方法名前面使用synchronized关键字修饰即可。
同步块:
class One {
void display(int num) {
System.out.print(""+num);
try {
Thread.sleep(1000);
}
catch(InterruptedException e){
System.out.println("中断");
}
System.out.println(" 完成");
}
}
class Two implements Runnable {
int number;
One one;
Thread t;
public Two(One one_num,int n) {
one=one_num; number=n;
t=new Thread(this); t.start();
}
public void run() {
synchronized(one) {
one.display(number);
}
}
} 同步块就是如果无法在相关方法前加synchronized修饰符,只需将对这个类定义的方法调用放入一个synchronized块内就可以了。
死锁:
当两个线程循环依赖于一对同步对象时将发生死锁。例如:一个线程进入对象ObjA上的监视器,而另一个线程进入对象ObjB上的监视器。如果ObjA中的线程试图调用ObjB上的任何synchronized 方法,就将发生死锁。死锁很少发生,但一旦发生就很难调试。
线程之间的通信:
实现线程之间通信的三个方法是
- wait() :让使用对象的线程停止运行,并进行等待,直到另一个使用该对象的线程运行notify()方法。
- notify():线程在对象中碰到notify()方法时,会唤醒使用相同对象的第一个碰到wait()的线程。
- notifyAll() :线程在对象中碰到notifyAll()方法时,回唤醒相同对象的所有线程,并让优先级最高的线程准备运行。
这三个方法仅在synchronized 方法中才能被调用。
守护线程:守护(Daemon)线程是在后台运行的线程。也就是说,只有当CPU有多余的时间时,该线程才会运行。我们可以在某个线程上setDaemon(true)方法来建立一个守护线程,如果执行setDaemon(false),该线程就不是守护线程了。使用isDaemon()方法来判断线程是否为守护线程。
第十四章:java集合框架
Java集合类都位于java.util包中
_set(集):对象容器中的对象没有顺序,且不能重复。
_list(列表):对象容器中的对象按照索引顺序排序,而且可以由重复的对象。
_Map(映射):对象容器中的元素包含一对“键对象--值对象”映射,其中键对象不能重复,值对象可以重复。
为支持对象的排序和遍历访问操作,java集合框架中又提供了几个接口:
_接口SortedSet为Set类型容器提供排序功能。
_接口SortedMap是为Map类型容器提供对键对象的排序。
_接口Iterator提供了集合对象进行遍历访问的遍历器。
_接口Comparable和Comparator用来实现集合中对象的排序。
Java集合框架中还提供了Collections和Arrays这两个工具类。
Collection接口和Iterator接口:
Collection接口:在集合框架中,集合(Collection)接口位于Set接口和List接口的最顶层,是Set接口和List接口的父接口。Collection接口中定义了Collection对象共有的一些基本方法,这些方法分为基本操作、批量操作和数组操作。
Iterator接口:遍历集合的接口。所谓遍历,是指从集合中取出每一个元素的过程。
_hasNext():如果集合中还有更多元素,该方法返回true
_next():返回集合中的下一个元素
_remove():删除iterator返回的最后一个元素
List接口:
_List中的元素是有顺序的
_List通常允许重复元素
_List的实现类通常支持null元素
_可以通过索引访问List对象容器中的元素
List接口的实现类具有共同的方法:
add() —— 向集合中添加元素(增)
remove() – 将元素从集合中移除(删)
get() —— 从集合中获取元素(查)
set() —— 修改集合中的元素(改)
size() —— 查看集合长度
泛型:所谓泛型就是允许在定义类、接口是指定类型形式参数,这个类型形式参数在声明变量、创建对象时确定(即传入的实际参数)。
LinkedList与ArrayList的选择:
ArrayList采用数组的方式存储对象,这种方式将对象放在连续的位置中,他有一个缺点就是对它们进行删除或者插入操作时非常麻烦。
LinkedList是实现了双向链表功能的列表,它将列表中的每个对象放在独立的空间,而且每个空间中还保存有上一个和下一个链接的索引。LinkedList不支持快速随机访问,如果要访问LinkedList中第n个元素,必须从头开始查找,然后跳过前面的n-1个元素。
因此,如果列表需要快速存取,但不经常进行元素的插入和删除操作时,选择ArrayList;如果需要对列表进行频繁的插入和删除时,应该选择LinkedList。
Set接口:
_Set类型容器中不能包含重复元素。当加入一个元素到容器中的时候,要比较元素的内容是否存在重复的,所以加入Set类型对象容器的对象必须重写equals()方法。
_元素可能有顺序,也可能没有顺序。
_因为元素没有顺序,所以不能基于索引访问Set中的元素。
Set接口的实现类具有共同的方法:
add() —— 向集合中添加元素(增)
remove(Object o) – 将元素从集合中移除(删)
size() —— 查看集合长度
在JDK1.5中增加了一种新型的循环,称之为for each循环,该循环大大简化了对于集合的遍历操作。
如:
for(String a : list){
System.out.println(a);
}
HashSet:1当遍历HashSet时,其中的元素是没有顺序的。
2HashSet中不允许出现重复元素。这里的重复元素是指有相同的哈希码,并且用equals()方法进行比较时,返回true的两个元素。
3允许包含null元素。
HashSet存储机制: 在基于哈希算法的集合类(HashSet、HashMap、HashTable等)中,每个能存储元素的位置称为桶(bucket),一个桶可以装多个元素。桶的位置由hashCode()方法计算得来的哈希码值来决定。不同哈希码值的对象存放在不同的桶中。一个桶中的多个对象具有相同的哈希码值。因此,当我们比较两个对象是否是同一对象时,首先根据调用对象的hashCode()方法计算对象的哈希码值,得到桶的位置,然后调用对象的equals()方法与桶中的每个对象进行对比,如果返回true,则表示两个对象是同一对象。如果我们只重写了equals()方法,而没有重写hashCode()方法,就找不到桶,也就无法进行对象的比较。同样,如果我们只重写了hashCode()方法,没有重写equals(),也就只能找到桶,无法进行比较。
TreeSet:TreeSet类不仅实现了Set接口,还实现了SortedSet接口,从而保证集合中的对象按照一定的顺序排序。必须注意的是:使用自然排序时,只能向集合中加入同类型的对象,并且这些对象必须实现了Comparable接口,否则程序会抛出异常。如果要实现自定义排序,就必须使用Comparator接口同时必须重写到TreeSet中的对象的compare(Object o1,Object o2)方法。Compare()方法可以用来对两个参数进行对比,如果o1小于o2,则返回-1;如果o1和o2相同,则返回0;如果o1大于o2,则返回1。
Map接口:
1Map接口不是Collection接口的继承。Map接口用于维护键/值对(key/value pairs)。每个条目包括单独的两部分 key、Value
2在Map中不允许出现重复的键.
3key 和 value可以是任何类的实例
Map接口的实现类都是关于键值对的操作,它们拥有一些共性的方法:
_put() 将键值对存入集合
_get() 根据键取出元素的值
_keySet() 将Map中的所有键取出形成一个Set
_values() 将Map中的所有值取出形成一个Collection
_remove() 根据键移除值
工具类Collections和Arrays:
1Java集合框架中提供了一个操作Set、List和Map等集合的工具类Collections,该工具类中提供了大量方法,用于对集合元素进行排序、查询、修改等操作。
2Java集合框架同时也提供了Arrays类用于操作数组。Arrays类包含用来操作数组(比如排序和搜索)的各种方法。
古老的集合类与接口:
1Vector、Hashtable、Stack、Properties类以及遍历器Enumeration接口。
2Vector(向量)与ArrayList的用法几乎完全相同,二者之间的最大区别在于Vector类是线程安全的,而ArrayList不是。
3Hashtable与HashMap的用法也几乎相同,二者的关系完全类似于Vector与ArrayList的关系。Hashtable是线程安全的,而HashMap不是。 此外,Hashtable不允许key和value为null,而HashMap是允许的。
4Properties到现在还被频繁使用的一个类。该类用于处理属性文件。所谓属性文件就是将Map形式的键/值对数据存在一个扩展名为“.properties”的文本文件中,常用作软件的配置文件。
第十五章:Java图形用户界面编程
包 |
类 |
特点 |
java.awt |
CardLayout |
将组件象卡片一样放置在容器中,在某一时刻只有一个组件可见 |
java.awt |
FlowLayout |
将组件按从左到右而后从上到下的顺序依次排列,一行不能放完则折到下一行继续放置 |
java.awt |
GridLayout |
形似一个无框线的表格,每个单元格中放一个组件 |
java.awt |
BorderLayout |
将组件按东、南、西、北、中五个区域放置,每个方向最多只能放置一个组件 |
java.awt |
GridBagLayout |
非常灵活,可指定组件放置的具体位置及占用单元格数目 |
Javax.swing |
BoxLayout |
就像整齐放置的一行或者一列盒子,每个盒子中一个组件 |
Javax.swing |
SpringLayout |
根据一组约束条件放置子组件 |
Javax.swing |
ScrollPaneLayout |
专用于 JScrollPane,含一个Viewport,一个行头、一个列头、两个滚动条和四个角组件 |
Javax.swing |
OverlayLayout |
以彼此覆盖的形式叠置组件 |
Javax.swing |
ViewportLayout |
JViewport 的默认布局管理器 |
第十六章:java I/O系统
流的分类:
1)按照数据流的方向分,分别是输入流(InputStream)、输出流(OutputStream)
2)按照处理数据的最小单位的不同,分别是字节流(字节流以byte为最小单位进行数据运输。Io包中的字节流都继承自抽象类InputStream或OutputStream)、字符流(字符流是以char为最小单位进行数据运输。Io包中的字符流都继承自Reader或者Writer)
3)按照流的功能分,分别是节点流和处理流
文件类:File类是对文件系统中文件以及文件夹进行封装的对象,可以通过对象的思想来操作文件和文件夹。File类保存文件或目录的各种元数据信息,包括文件名、文件长度、最后修改时间、是否可读、获取当前文件的路径名,判断指定文件是否存在、获得当前目录中的文件列表,创建、删除文件和目录等方法。
InputStream的基本方法:
_int read() throws IOException读取一个字节以整数形式返回,如果返回-1已到输入流的末尾
_void close() throws IOException关闭流释放内存资源
_long skip(long n)throws IOException 跳过n个字节不读
OutputStream的基本方法:
_void write(int b)throws IOException 向输出流写入一个字节数据
_void flush() throws IOException将输出流中缓冲的数据全部写出到目的地
Writer的基本方法:
_void write(int c)throws IOException 向输出流写入一个字符数据
_void write(String str)throws IOException将一个字符串中的字符写入到输出流
_void write(String str,int offset,int length)
将一个字符串从offset开始的length个字符写入到输出流
_void flush() throws IOException
Reader的基本方法:
_int read() throws IOException读取一个字符以整数形式返回,如果返回-1已到输入流的末尾
缓冲流:缓冲流要套接在相应的节点流之上,提高了读写的效率。
此处理流的构造方法都得传相对应的基类类型
BufferedReader:提供了readLine方法用于高校读取一行字符串
BufferedWriter:提供了newLine用于写入一个行分隔符也就是换行
BufferedInputStream 没多大用处
BufferedOutputStream 没多大用处
转换流:主要作用将字节流转换成字符流。用处较大!
转换流在构造时可以指定其编码集合
InputStreamReader需要和InputStream套接
OutputStreamWriter需要和OutputStream套接
例:OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(文件路径);
方法例:osw.getEncoding(); 获得流的编码方式
数据流与字节数组流:数据流主要为实现可以存取Java原始数据类型如long,boolea.数据流是字节流DataInputStream需要和InputStream套接
DataOutputStream需要和OutputStream套接
DataInputStream方法:readBoolean()readInt()read……()……
readUTF():网络传输常用方法 读一个Unicode字符串
DataOutputStream方法与DataInputStream基本对应为写的方法
例://此构造函数等于已可以往一个字节数组里输入内容
ByteArrayOutputStream baos = new ByteArrayOutputStream ();
//此方法为获取一个字节数组方法返回字节数组
baos.toByteArray();
Print流:
Print流只有输出流无输入流,PrintWriter和PrintStream分别针对字符字节
两个类提供了重载的Print和Println方法用于多种数据类型的输出
PrintWriter和PrintStream的输出操作不会抛出异常
PrintWriter和PrintStream有自动flush功能
Object流:
transient关键字为不序列化此成员变量
需要序列化的类必须实现Serializable接口
主要方法:writeObject(Object);readObject();
读出为Object类型需要强转数据类型
第十七章:java网络编程
TCP/IP协议:
常用的端口:
协议 |
端口 |
Telnet协议 tel
|
23 |
简单邮件传输协议 smtp
|
25 |
文件传输协议 ftp
|
21 |
超文本传输协议 http
|
80 |
TCP协议:代表传输控制协议,它允许两个程序之间进行可靠的通讯。较可靠的双向流协议
发送任意数量的数据;提供消息确认、错误检测和错误恢复等服务。
UDP协议:代表用户报文协议,它是一种非连接协议,允许两个程序之间进行不可靠的通讯。
套接字(Socket):
是一种抽象层,应用程序通过它来发送和接收数据。一个TCP/IP套接字由一个IP地址、一个端对端协议(TCP或UDP协议) 以及一个端口号唯一确定。
InetAddress类:
该类没有构造方法,所以不能直接利用new 关键字创建对象。我们可以使用以下常用方法来获取该类对象。
_getLocalHost() 返回表示本地主机InetAddress对象
_getByName(String hostName) 根据主机名返回对象
_getAddress() 返回IP地址
_getHostName() 取得IP地址代表的主机名
套接字(Socket)编程:
服务端编写步骤:
1.利用ServerSocket建立对服务端某个端口的监听。
如:ServerSocket server = new ServerSocket(8000);
2.利用accept方法创建服务端Socket
如:Socket socket = server.accept()
3.利用已建立的socket创建输入输出流
如:BufferedReader br = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
PrintWriter pw = new PrintWriter( socket.getOutputStream(), true);
4.关闭输入输出流,关闭socket,关闭server
如: br.close();
pw.close();
socket.close();
客户端编写步骤:
1.创建客户端Socket向服务器发起连接请求
如:Socket socket = new Socket (“127.0.0.1”, 8000);
2..利用已建立的socket创建输入输出流
如:BufferedReader br = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
PrintWriter pw = new PrintWriter( socket.getOutputStream(), true);
3.关闭输入输出流,关闭socket,关闭server
如: br.close();
pw.close();
socket.close();
UDP编程:
接收数据报包需要执行如下步骤:
1. 创建一个足够大的字节数组,用于存储要接收的包的数据。
2. 使用该字节数组实例化一个DatagramPacket对象。
3. DatagramSocket被实例化,它被指定该套接字要绑定到的本地主机上的一个端口。
4. 调用DatagramSocket类的receive()方法,将DatagramPacket对象传入该方法。这将导致执行线程阻塞,直到接收到一个数据报包或者发生了超时。
发送数据报包需要执行如下步骤:
1. 创建一个足够大的字节数组,用于存储要发送的包数据,用该数据填充数组。
2. 创建一个新的DatagramPacket对象,用于存储上面的字节数组,以及服务器名和接收者的端口号。
3. DatagramSocket被实例化,它被指定套接字要绑定到本地主机的哪个端口。
4. DatagramSocket类的send()方法被调用,传入DatagramPacket对象。
URL类与URLConnection类:
URL:一个URL实际上是一类URI(统一资源标识符)。URI是标识一个资源,但不包括如何访问该资源的信息。URL标识一个资源以及访问该资源的协议。一个URL可以分为如下几个部分:协议://主机:端口/路径?查询字符串#锚点引用
URLConnection:使用URL类的openConnection()方法,可以连接到一个URL,并与资源进行通讯。openConnection()方法返回一个java.net.URLConnection,URLConnection是一个抽象类,其子类代表不同的URLConnection。
第十八章:java数据库编程
JDBC工作过程:
JDBC的四种驱动程序:
1JDBC-ODBC Bridge:
_ JDBC-ODBC桥
_ 由SUN公司提供通用的驱动,能访问各种数据库,但效率极低。
2Native-API partly-Java driver:
_ 本地库Java驱动程序
_ 执行效率高,客户端必须安装本地驱动,维护不方便。
3net-protocal all-Java driver(JDBC Proxy)
_ 网络协议纯Java驱动程序(通用)
_ 客户端不必安装本地库,使用方便,但性能相对较低。
4native-protocal all-Java driver:
_ 本地协议完全Java驱动程序
_ 将JDBC调用转化为特定数据库的网络协议,效率很高。
JDBC编程步骤:
步骤一:根据应用程序所用的数据库,选择jdbc驱动程序类型。
步骤二:连接到数据库,得到Connection对象;
步骤三:通过Connection创建Statement对象;
步骤四:通过Statement对象提交SQL语句;
步骤五:操作结果集;
步骤六:回收数据库资源。
连接到数据库:
在JDBC中,有两种方式可以设置一个到数据库的连接:
1使用java.sql.DriverManager类的静态方法getConnection(),该方法带一个URL代表数据源的名称。
2JNDI(java命名服务)查找数据源的名称,JNDI返回一个javax.sql.DataSource对象,然后使用DataSource类的getConnection()方法。
几种常见的数据库URL格式:
Mysql-------------------------jdbc:mysql://主机名:端口号、数据库名
Oracle-------------------------jdbc:oracle:thin:@主机名:端口号:数据库名
SQL Server------------------jdbc:sqlserver://主机名:端口号:databaseName=数据库名
Statement对象:常用于执行一个肯定只发生一次,并且没有参数的SQL语句。Connection接口包含三种方法用于创建Statement对象:
_ Statement createStatement():创建一个默认的Statement对象,该Statement对象的结果集是只读的并且只能向前滚动的。
_ Statement createStatement(int resultSetType , int countcurrency):创建一个给定结果集类型和并发类型的的Statement对象。
_ Statement createStatement(int resultSetType , int countcurrency ,int holdability):创建一个给定结果集类型、并发类型和可保存性类型的Statement对象。
Statement接口定义和执行SQL语句的一些方法:
_ ResultSet executeQuery(String sql):执行一个返回单个结果集的SQL语句,只能改方法执行select语句。
_ Int executeUpdate(String sql):使用该方法执行不返回结果集的DDL和DML语句。如果执行的是DML,则返回值为执行SQL语句影响的行数。如果是执行DDL语句,则返回值为0;
Boolean execute(String sql):该方法可以执行任何SQL语句。
批量操作:
_ public void addBatch(String sql):用于将制定SQL语句添加到当前批中。
_ public int[] executeBatch():执行批中所有sql语句。返回值数组与批中的语句相关,通常代表每个批中语句执行成功后的影响行数。
PreparedStatement:如果我们有SQL语句要执行很多次,不管这个语句是否带有参数,都应该使用PreparedStatement。PreparedStatement有两个方法设置参数:
1)void setXxx(int parameterindex, Xxx value):如果知道参数的类型,则通过该方法设置参数的值。
2)void setObject(int parameterindex, Object o):如果不知道参数的类型,则通过该方法设置参数的值,由PreparedStatement来负责类型转换。
ResultSet:ResultSet接口中的方法可以分为三类:
1)用于移动记录指针的导航方法,包括next()、first()、last()、previous()、beforeFirst()、afterLast()等;
2)用于查看记录指针所在行的某一列的值的getXxx()方法。这些get方法检索数据,并将数据转换为java原始数据类型。每种数据类型都是具有独立的getXXX方法。
3)用于更新当前行列数据的update方法。
Sql数据类型 |
Java类型 |
CHAR |
String |
VARCHAR |
String |
BIT |
java.math.BigDecimal |
INTEGER |
boolean |
BIGINT |
int |
REAL |
float |
FLOAT |
double |
DOUBLE |
double |
DATE |
java.sql.Date |
TIME |
java.sql.Time |
TIMESTAMP |
java.sql.Timestamp |
第十八章:java与XML解析技术
XML(eXtensible Markup Language)是万维网联盟(World Wide Web Consortium W3C)定义的一种可扩展标志语言。
可扩展性指允许用户按照XML规则自定义标记(tags标签)。
强项:轻松表达多层结构的数据;可扩展。
优点:平台无关,语言无关。设计目标是描述数据并集中于数据的内容,与显示分离。
提醒:不能用XML来直接写网页。即便是包含了XML数据,依然要转换成HTML格式才能在浏览器上显示。
语法规则:
XML文件有且仅有一个根标记,其他标记必须封装在根标记中,文件的标记必须形成树状结构。
大小写敏感。
标记的属性必须用""或''括起来。
XML细节:
一、 声明
大多数XML文档以XML声明作为开始,它向解析器提供了关于文档的基本信息。
建议使用XML声明,但它不是必需的。如果有的话,那么它一定是文档的第一行内容。
如:
声明最多可以包含三个名称-值对(许多人称它们为属性,尽管在技术上它们并不是)。
问号与xml之间不能有空格。
1)version 是使用的XML 版本:1.0, 1.1
2)encoding 是该文档所使用的字符集。该声明中引用的ISO-8859-1 字符集包括大多数西欧语言用到的所有字符。
默认字符在UTF-8字符集中,这是一个几乎支持世界上所有语言的字符和象形文字的Unicode标准。
3)standalone(可以是yes或no)定义了是否孤立处理该文档。
如果XML文档没有引用任何其它文件,则可以指定standalone="yes"。
如果XML文档引用其它描述该文档可以包含什么的文件(如DTD),则standalone="no"。默认值为"no"
二、 标记
左尖括号“<“和右尖括号“>“之间的文本
1. 在< >中的称为开始标记;在 >中的称为结束标记
2. 空标记:不包含元素的标记。空标签必须以“/>”结束。格式:<空标记的名称/> <空标记的名称 属性列表/>
注意:
除空标记外,标签必须成对:有始有终。所有的开始标签和结束标签必须匹配。
在标记符“<“和"标记的名称"之间不能含有空格。在标记符"/>"前面可以有空格或回行。
标签必须嵌套正确。
XML标记必须遵循下面的命名规则:
1.名字中可以包含字母、数字以及其它字母或文字;还可包含下划线(_)、点(.)、连字符(-)
2.名字不能以数字开头;可以用字母、文字或者下划线开头。
3.名字不能以字母xml (或XML或Xml ..)开头;
4.名字中不能包含空格。
三、 元素
位于开始标记与结束标记间
一份文档有且只有一个根元素。
根元素下的所有元素叫“子元素”。
标签必须嵌套正确。
不包含自子元素的元素叫“叶子”;包含子元素的元素叫“分支”。
如:
四、 属性
一个元素的开始标志中的名称-值对
所有的属性值必须位于单引号或双引号中。
每一个元素的属性不允许出现超过一次。
开始标志内,类似赋值语句
如:
五、 注释
注释可以出现在文档的任何位置。(但不建议放在声明前面,部分浏览器会报错)
注释以 结束。
注释内不能包含双连字符(--);除此之外,注释可以包含任何内容。
注释内的任何标记都被忽略
六、 处理指令
处理指令是为使用一段特殊代码而设计的标记,简称为PI。
大多数XML文档都是以XML声明开始,该声明本身就是特殊的处理指令。
处理指令对应用程序特定的数据进行编码。一条处理指令包含一个目标,后跟数据。用和?>定界符将处理指令包起来。
目标确定应用程序,而对应用程序不能识别的目标,其会忽略这些处理指令。
七、 实体
XML 规范预定义了五个实体。
< ==== <
> ==== >
" ==== ”
' ==== ‘
& ==== &
自定义实体:在DTD中定义实体标志"实体内容">
在xml中引用自定义实体,用 &实体标志; 代表实体内容。
另外,无法从键盘输入的字符可以使用字符引用,就是用字符的Unicode代码点来引用该字符。
以""开始字符引用,以分号结尾,x必须为小写,使用十六进制。如:= 表示等于号。
也可以使用字符引用来引用<,>,',",& "
查看字符的代码点(附件->系统工具->字符映射表)。
八、 CDATA
当一段文本中出现很多实体引用和字符引用时,会导致文本数据的读写困难,CDATA段就是为了解决这一问题引入的。
DATA区段开始于"结束于 "]]>"
CDATA内部的所有东西都会被解析器忽略解析,不用检查它的格式。
但是CDATA段中不能嵌套另一个CDATA段。
九、 属性
属性是标记的属性,可以为标记添加附加信息。
(1)属性的组成
属性是一个名值对,必须由名称和值组成,属性必须在标记的开始标记或空标记中声明,用"="为属性指定一个值。
语法如下:
<标记名称 属性列表/>
<标记名称 属性列表>XXX标记名称>
例如: <桌子width="40" height='100'/>
(2)使有属性的原则
属性不体现数据的结构,只是数据的附加信息;
一个信息是作为一个标记的属性或子标记,取决于具体问题,不要因为属性的频繁使用破坏XML的数据结构。
下面是一个结构清晰的XML文件:
<楼房height="23m" width="12m">
<结构>混凝土结构>
<类别>商用类别>
楼房>
下面是一个结构不清晰的XML文件:
<楼房height="23m" width="12m" 结构="混凝土"建筑商="华海集团"类别="商用">楼房>
十、 名称空间/包
XML文件允许自定义标记,所以可能出现同名字的标记,为了区分这些标记,就需要使用名称空间。
名称空间的目的是有效的区分相同的标记,其实并不真实存在。
语法: 声明有前缀的名称空间 xmlns:前缀名=名称空间的名字
声明无前缀的名称空间 xmlns=名称空间的名字 (缺省)
注意:当且仅当它们的名字相同时称二个名称空间相同,也就是说,对于有前缀的名称空间,如果二个名称空间的名字相同,即使前缀不相同,也是相同的名称空间,返之同然。前缀只是方便引用而已。
一、DTD验证
文档类型定义(Document Type Definition)
DTD定义了XML文档内容的结构,保证XML以一致的格式存储数据。精确的定义词汇表,对XML的内容施加约束。
符合DTD的规范XML文档称为有效的文档。由DTD定义的词汇表以及文档语法,XML解析器可以检查XML文档内容的有效性。
规范的XML文件不一定是有效的;有效的一定是规范的。
XML解析技术:
一、 DOM文档对象模式
1.DOM特点:
以树型结构访问XML文档。 一棵DOM树包含全部元素节点和文本节点。可以前后遍历树中的每一个节点。
整个文档树在内存中,便于操作;支持删除、修改、重新排列等多种功能。
将整个文档调入内存(包括无用的节点),浪费时间和空间。
一旦解析了文档还需多次访问这些数据;硬件资源充足(内存、CPU)情况下使用。
2.DOM树与节点
XML文档被解析成树型结构。
树由节点组成。共有12种不同的节点。
节点可以包含其他节点(依赖于节点的类型)。
父节点包含子节点。叶子节点没有子节点。
3.节点类型
Document node 包含:一个根Element节点。一个或多个处理指令节点。
Document Fragment node
Element node包含:其他Element节点。若干个Text节点。若干个Attribute节点。
Attribute node 包含:一个Text节点。
Text node
Comment node
Processing instruction node
Document type node
Entity node
Entity reference node
CDATA section node
Notation node
二、 SAX基于事件处理模式
解析器向一个事件处理程序发送事件,比如元素开始和元素结束,而事件处理器则处理该信息。
然后应用程序本身就能够处理该数据。原始的文档仍然保留完好无损。
处理器(遍历XML) ###########################]]>
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
public class TestSAXParser {
/** 基于SAX方式解析XML文档*/
public static void main(String[] args)
throws SAXException,ParserConfigurationException,IOException{
SAXParserFactory factory = SAXParserFactory.newInstance(); //创建SAX解析器工厂
factory.setValidating(true); //让error方法生效
SAXParser parser = factory.newSAXParser(); //生成一个具体的SAX解析器
parser.parse("src/file/student.xml",new XMLreader()); //开始解析
}}
class XMLreader extends DefaultHandler {
// 只需覆盖我们感兴趣的方法
private int counter = 0;// 定义一个计数器,保存XML文档触发事件的次数
@Override // 文档开始事件触发
public void startDocument() throws SAXException {
counter++;
System.out.println(counter + ".解析XML文件开始...");}
@Override // 文档结束事件触发
public void endDocument() throws SAXException {
counter++;
System.out.println("/r/n"+counter + ".解析XML文件结束...");}
@Override // 元素开始事件触发
public void startElement(String uri, String localName, String qName,
Attributes atts) throws SAXException {
counter++;
System.out.print(counter+".<"+qName);
for(int i=0; i
System.out.print(" "+atts.getLocalName(i)+"="+atts.getValue(i));
}System.out.print(">"); }
@Override // 元素结束事件触发
public void endElement(String uri, String localName, String qName) throws SAXException {
counter++;
System.out.print(counter +"."+qName+">");}
@Override // 文本事件触发 打印时尽量不要换行,否则很难看
public void characters(char[] ch, int start, int length)throws SAXException {
counter++;
String text = new String(ch, start, length); //当前元素的文本值
System.out.print(counter + ".Text=" + text);}
@Override //这是可恢复错误。需在SAXParserFactory设置有效性错误才能生效
public void error(SAXParseException e) throws SAXException {
System.out.println("xml文档有效性错误:"+e);}
@Override //严重错误
public void fatalError(SAXParseException e) throws SAXException {
System.out.println("xml文档严重的有效性错误:"+e);}
}
处理器(遍历XML)结束###########################]]>
三、 DOM
遍历方式###########################]]>
import java.io.File;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**基于DOM的解析XML文档*/
public class TestDOMParser {
public static void main(String[] args)
throws ParserConfigurationException,SAXException,IOException{
//创建一个DOM解析器工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//从工厂中生成一个DOM解析器;throws ParserConfigurationException
DocumentBuilder builder = factory.newDocumentBuilder();
//绑定需要解析的XML文件
File xmlFile = new File("src/file/student.xml");//相对地址,相对于这个工程
//开始解析 ;throws SAXException,IOException
Document document = builder.parse(xmlFile);
//取出唯一的根元素
Element rootElement = document.getDocumentElement();
//调用业务方法: 遍历根元素
printElement(rootElement);
}
/** 遍历元素,包含: 子元素、属性、文本内容*/
private static void printElement(Element e){
//打印出元素的标签名
System.out.print("<"+e.getTagName());
//获取开始标签的属性
NamedNodeMap attMap = e.getAttributes();
//循环遍历所有的属性
for (int i=0;i
Attr attr = (Attr)attMap.item(i);
System.out.print(" "+attr.getName()+"="+attr.getValue());}
System.out.print(">");
//获取当前元素的所有子节点
NodeList nl = e.getChildNodes();
for (int j=0;j
Node n = nl.item(j);
if (Node.ELEMENT_NODE==n.getNodeType()){
printElement((Element)n);//递归调用,以遍历下一个元素
} else {
System.out.print(n.getTextContent());
}
}
//打印结束标签
System.out.print(""+e.getTagName()+">");
}}
遍历 完毕##########################]]>
比较DOM与SAX:
DOM:处理大型文件时其性能下降的非常厉害。这个问题是由DOM的树结构所造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档装入内存,适合对XML的随机访问
优点:1.提供随机定义元素操作,来回移动指针
2.将整个XML文件一次性加载到内存,形成虚的内存树
缺点:1.如果XML文件较大,内存空间占用较大
2.强制将较大的XML文件加载到内存中,有可能损害文件
3.功能通用性
SAX:不同于DOM,SAX是事件驱动型的XML解析方式。它顺序逐行读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问