javase面向对象编程

Java语言学习笔记
一、 Javase基础语法
Java的数据类型有四类八中:

1、字符型char
*Char型数据用来表示通常意义上的字符如:char eChar = ‘a’ char cChar = ‘中’;
*Java中的字符采用Unicode(全球语言统一字符编码,它分为8位的和16位的),因为每个字节有8位,每个字符占用两个字节,所以可用16进制编码表示,char c1 = ‘\u0061’;
*java语言中还允许使用转义字符‘\’来将其后的字符串转化成其他的含义:
如:char c2=’\n’; --\n代表换行符
*补充二级制、十进制、十六进制相互转化:
1101-----1x1+0x2+1x4+1x8
1101-----1+4+8-----13
1101-----D(二进制在转化成为16进制的时候,最好先将二进制转换成为十进制,然后在转为十六进制,A在十六进制中对应十进制中的10)
2、整数类型byte short int long
*java中各个整数类型有各自的表示范围和字段长度,其不受具体操作系统的影响,以保证java系统的可移植性。
*java语言整形常量的三种表示形式:
十进制整数:如:12 -314 0.
八进制整数:如:要以0开头,如:012
十六进制整数:如:要以0x或者0X开头:如:0x12
*java语言的整数常量默认为int型,声明long型常量可以‘l’或者‘L’
如:int i1=600 --正确
long l1=88888888L ---必须加L否则会出错
类型 占用存储空间 表数范围
Byte 1字节 -128----127
Short 2字节 -2e15------2e15-1
Int 4字节 -2e31------2e31-1
long 8字节 -2e63------2e63-1
3、浮点类型:float double
*与整数类型类似,java浮点类型有固定的表数范围,和字段长度,不受平台影响
*java浮点类型常量有两种表示形式
十进制形式:如:3.14 314.0 .314
科学计数法形式:如:3.14e2 2.14E2 100E-2
*java浮点型常量默认为double型,如声明一个常量为float型,则需要在数字后面加f或者F如:
double d = 12345.5 -----正确
float f = 12.3f ---------正确 必须加f否则错误
下面列出浮点数的类型:
类型 占用存储空间 表数范围
Float 4字节 -3.403E38---3.408E38
Double 8字节 -1.798E308---1.798E308
4、boolean类型它有两个值true和false
5、基础数据类型的相互转化
* Boolean类型不可以转换成其他的数据类型
* 整形,字符型,浮点型的数据在混合运算中相互转换,转化事遵循以下原则:
*byte, short ,char ->int ->long->float->doule
*byte, short ,char之间不会相互转换,他们三者在计算时首先会转换为int类型
* 容量大的数据类型转换成容量小的数据类型时,要加上强制转化符,但可能造成数据精度降低或溢出,使用时要格外注意。
*有多种类型的数据混合运算时,系统首先自动的将所有数据转换成这些类型中容量最大的那一种数据类型,然后再进行计算
*实数常量(如:1.2)默认为double 整数常量(如:123)默认为int
*注:1、byte short char 在做运算时二话不说先转化成int类型再做运算
2、一般情况下类型容量大的转化成类型容量小的时候比如long转换成int时,在内存中的表现是直接将long中比int多出的那4个字节给去掉,剩下的4个字节表示几转换后就是几。
3、double转换成float时则会出现问题,因为浮点类型中专门用一位来存放小数点,所以这样转化是转化不过来的,通常转化后得到的结果为infinity(无穷大)
4、float直接转化成long类型时,在内存中是直接将浮点类型中小数点后面直接砍掉就可以了 ---如:int i = 1, j = 10 那么i/j=1/10 = 0 因为i和j都为int类型,int和int类型计算,结果也为int类型但是计算结果带有小数点为double类型,所以这里存在强制转化将double类型强制转为int所以去掉小数点后面所有的值,所以计算结果为0

二、编程格式:
1、大括号要对齐
2、遇到{就缩进,Tab/Shift+Tab
3、程序块之间要加空行
4、并排语句之间加空格
5、运算符两侧加空格(有特定条件)
6、{前有空格
7、成对编程(主要指括号)
三、运算符:
1、算术运算符:+,-,*,/,%,++,--
2、关系运算符:>, <, <=, >=, ==, !=
3、逻辑运算符:!, &, |, ^, &&, ||
4、位运算符:&, | , ^, ~, <<, >>, >>>
5、赋值运算符:=
6、扩展赋值运算符:+=, - =, *=, /=
7、字符串连接运算符:+
8、三目运算符:x ? y : z 如果条件x成立则返回y值,否则返回z值
注:“+”运算符两侧的操作数种只要有一个是字符串(String)类型,系统会自动将另一个操作数转换为字符串然后再进行连接,在用System.out.println()打印的时候,不管是什么类型的数,打印的时候都先转换成字符串然后打印,如:int c = 12; System.out.println(c);
四、表达式:
*表达式是符合一定语法规则的运算符和操作数的序列
a, 5.0+a, (a-b)*c-4, i<30&&i%10!=0
*表达式的类型和值:
1、对表达式中操作数进行运算得到的结果称为表达式的值
2、表达式的数据类型即为表达式的类型
*表达式的运算顺序:
1、 应按照运算符的优先级从高到底的顺序进行
2、 优先级相同的运算,按照事先约定的结合方向进行、
五、语句:
1、条件语句(分支语句)根据条件不同,执行不同的语句
If…
If…else…
If….elseif…
If…elseif…elseif…else…
Switch…
2、 循环语句—重复执行某些动作
For
While
Do…while…

Break:语句用于终止某个语句块的执行,用在循环语句体中,可以强行退出循环;
Continue:语句用在循环语句体中,用于终止本次循环过程,跳过循环体中continue语句下面未执行的循环,开始下一步循环过程

六、方法:
Java中的方法,类似于其他语言的函数,是一段用来完成特定功能的代码片段,声明格式: [修饰符1,修饰符2,…] 返回值类型 方法名(形式参数列表){
Java语句;…
}
形式参数:在方法被调用时,用于接收外界输入的数据。
实参:调用方法时实际传递给方法的数据:
返回值,方法在执行完毕后返还给调用它的环境的数据。
返回值类型:事先约定的返回值的数据类型,如无返回值,必须给出返回值类型为void

Java语言中使用下述形式调用方法:对象名.方法名(实参列表);
实参的数目、数据类型和次序必须和所调用的方法声明的形参列表匹配
Return 语句终止方法的运行并指定要返回的数据。如果一个方法没有返回值,但是还有一个无条件的return语句,那么在这里就结束了方法的运行如:
无条件:Public void test(){return System..out.println(num);}这个方法后面的System…语句直接不能被执行,因为return 语句直接将方法结束,所以这样会报错,除非是带有条件的return;
有条件: public void test(int num){ if (num > 3) return; System..out.println(num);}在这种情况下,它后面的语句是可以被执行的,但是前一种会报错,因为后面的不能被执行,它会报后面的语句无法访问。
Java中进行函数调用中传递参数时,遵循值传递的原则:
基本类型传递的是该数据本身,引用类型传递的是对对象的引用,而不是对象本身

递归调用指的是:方法执行过程中出现该方法本体的调用
七、面向对象的思想
Java是一个纯面向对象的思想的语言,面向对象的思想是从现实世界中客观存在的事物出发来构造软件系统,并在软件的构造过程中尽可能运用人类的自然思维方式,面向对象更加强调运用人类在日常的思维逻辑中经常采用的思想方法和原则,如抽象、分类、继承、聚合、多态等。
面向对象的编程考虑的时候不能在考虑第一步该干什么,第二步该干什么…那是面向过程语言的写法,而应该考虑的是1.在这个问题里面有哪几个类,哪几个对象,2.考虑每种类和对象都应该有哪些属性和方法3.考虑的是这些类和类之间具备了怎样的关系。
1、类与对象的概念:
类:一类事物的一个抽象,即一类对象的模板,它是描述同一类型对象的抽象概念,它具有静态属性(成员变量或者属性)动态属性(方法)
对象:可以看成一个类的具体的实例
2、类与类及类与对象的关系:
(1) 类与对象:一个类可以通过实例化得到这个类的具体的一个实例或者称为对象,而从多个对象中抽象出它们共同的静态或动态的属性,就可以得到这些对象的一个类
(2) 类与类的关联关系:关联关系是最弱的一种关系,反映到代码中就是一个类的对象中的方法参数是另外一个类的对象
(3) 类与类的继承关系:只要是能把“什么什么是什么什么”这句话能说通了,那么这两个类就是继承关系
(4) 类与类的聚合关系:聚合关系说白了就是一个整体和部分的关系,如果能把什么什么是什么什么的一部分能说通的话,那么它们的关系就是聚合关系;聚合关系可以细分为两种关系,一种叫做聚集关系:如球员是球队的一部分,是一种松耦合的关系,部分之间没有必然的联系,一种叫组合关系,如胳膊是人身体的一部分,是一种强耦合,部分之间密不可分,互不分开。
(5) 类和接口的实现关系:即一个类实现了某个接口,这种关系就叫做实现关系
(6) 类和类的多态关系:多态主要涉及三个概念:1,继承2,重写,3,父类引用指向子类对象(又叫动态绑定或者迟绑定)
3、类的定义:
用class关键字定义一个类,类的定义主要由两部分组成---成员变量和方法
(1)声明成员变量的格式为:private int num; 成员变量可以使用java中的任何一种数据类型(包括基本类型和引用类型);在定义成员变量的时候可以对其初始化,如果不对其初始化,java默认给其设置初始值,即默认值如下:

成员变量类型 默认值
byte: 0
short 0
int 0
long 0L
char ‘\u0000’
float 0.0F
double 0.0D
boolean false
所有引用类型 null
成员变量的作用范围为整个类体
(2)声明方法的格式为:public void test(){}
4、引用
Java语言中除了那四类八种的基本类型外剩下的所有类型都是引用类型,java中的对象是通过引用对其操作的。通常的基础类型在内存中只占一块地方,即只在栈中分配一块内存用于存放基本类型的值;但是引用类型在内存中占用两块内存,类是存放在代码区的,当使用一个类new一个对象(引用)时首先会在栈内存中为这个引用型的变量(局部变量)分配一块内存空间,同时在堆内存中也分配一块内存,用来存放new出来的对象,而栈内存中则存放着这个对象的地址
5、对象的创建和使用:
必须使用new关键字来创建对象
使用对象(引用). 成员变量 来引用对象的成员方法
使用对象(引用). 方法(参数列表)来调用对象的方法,如果这个方法不是static的话,那么要想调用它就必须通过一个对象来加‘.’来调用了它,而要是加了static的方法,那么这个方法就不属于任何一个对象了,这时它属于这个类,那么在这个类中调用这个方法时,直接写这个方法的名称即可
同一个类的每一个对象有不同的成员变量存储空间
同一个类的每一个对象共享该类的方法
6、构造方法:
使用new加构造方法创建一个新对象
构造函数是定义在java类中的一个用来初始化对象的函数(方法)
构造函数必须与类同名切不能有返回值,连void都不能有
如果定义类的时候没有定义构造方法,那么java默认给这个类定义一个不含任何参数的空构造方法,但是如果这个类定义了构造方法,那么java就不会给出默认的构造方法,这样你想主动去调用java给出的默认构造方法是不能调用的,是会报错的。还有就是如果给构造方法前面不小心加上了一个void,或者返回值类型,那么这样这个构造方法就变成了普通的方法了

注:方法一旦被调用结束后在内存中分配的局部变量的空间就不在存在,但是在堆内存中的对象要是还被其他的变量引用,那么所占的内存却保留了下来

7、命名规范:
类名首字符大写
变量名和方法名首字母小写
命名采用驼峰标志
8、垃圾回收:
一般局部变量使用完后立即释放所占的栈内存空间,或者方法中的参数在方法调用结束后,对应于栈内存中的空间也被释放,那么堆内存中的对象(包括成员变量)什么时候消失呢,当堆内存中的对象没有任何引用型变量来引用它的时候,这时候它会等待垃圾收集器的收集,从而释放堆内存空间,这就是所谓的垃圾回收

注:在写程序时要注意封装一个类的时候,属于这个类的属性和方法就封装在这个类中,不属于的不应该往这个类中封装,如:测试的类是用来测试的,不能在一个非测试的纯类中添加测试类中定义的成分(属性)

9、方法的重载(overload)
一个类中可以定义相同的名字,但参数列表不同的多个方法,调用时会根据不同的参数表选择对应的方法。简单的说就是:方法名相同,参数列表不同的就构成重载,和返回值类型不同没有关系,参数列表不同指的是参数类型或者参数个数不同,如果两个方法的参数列表一样,方法名称一样,但是返回值不一样,那么这两个方法不能构成重载,反而编译器会报错,试想一下,以前讲的,一个方法有返回值,但是这个返回值可以不用,那么如果出现这种情况,那么调用两个方法都不用返回值,这样编译器就会分辨不出调用哪个方法了,所以会报错。构造方法也能进行重载

10 关键字
This: (一般出现在方法里面)
在类的方法定义中使用的this关键字代表使用该方法的对象的引用。
当必须指出当前使用方法的对象是谁时必须要使用this
有时候使用this关键字可以处理方法中成员变量和参数重名的情况
This可以看做是一个变量,它的值是当前对象的引用
Super(对基类对象的引用)
This是在方法中调用这个方法的所属对象的引用,而super指的是:子类继承父类后,在子类的对象中会包含这个父类的对象,这个super就是对这个父类的一个引用(在new一个子类对象时,在这个子类对象中会包含一个父类的对象)通过super.xxx可以在子类中调用父类的成员(成员变量和方法)
Static:
在类中,用static声明的成员变量为静态成员变量,它为该类的公用变量,在第一次使用时被 初始化,对于该类的所有对象来说,static成员变量只有一份
用static声明的方法为静态方法,在调用该方法时,不会将对象的引用传递给它所以在static方法中不可访问非static的成员
静态方法不再是针对某个对象调用,所以不能访问非静态成员
可以通过对象引用或者类名(不需要实例化)访问静态成员
任何一个对象都可以访问static修饰的成员变量,因为他属于所有对象所共有,所有对象访问时都访问的是同一块内存空间,即便是没有对象,也可以通过类名访问这个变量,因为它本身就属于类,同时不属于某一个对象私有,而是所有类共有的。
其实静态的成员变量可以被用来做计数用,可以用来统计这个类new出来了多少对象
静态的方法中不能使用非静态的变量,因为非静态的变量只能通过对象来访问,但是在静态方法中,编译器不会为了访问非静态方法帮你创建出一个对象来,所以,不能在静态方法中访问非静态的变量。以此类推,静态的方法中不能访问非静态的成员,包括变量和动态方法,
成员变量是在对象被new出来的时候,才在堆中给分配内存的。

非静态成员(变量和方法)专属于对象,想访问他们必须要new个对象出来

*Package和import语句:
为便于管理大型软件系统中数据众多的类,解决类的命名冲突问题,java引入包(package)机制,提供类的多重命名空间
*package语句作为java源文件的第一条语句,指明该文件中定义的类所在的包(若缺省该语句,则制指定为无名包,即所谓的裸体类,裸体类很容易和其他类产生冲突)
它的格式为:
Package pkg1[.pkg2[.pkg3…]];
Java编译器把包对应于文件系统的目录管理,package语句中,用‘.’来指明包(目录)的层次,例如使用语句:
Package com.sxt;
则该文件中所在的类位于.\com\sxt目录下
*如果要将一个类放在包里面,第一句话写package,package后面想跟多少层包就跟多少层包,但是编译出来的这个类的class文件必须位于正确的目录下面(和我们的包的层次要一致),如果想在另外一个类中使用这个类,那么必须加上包名把这个类的名称写全了,才能调用,但是如果嫌每次都要写很长的类名而感到麻烦,则可以使用import关键字,将这个包中的这个类导入到要使用的类中,也可以使用*将这个包下的所有类都导入到使用类中,使用*进行导入时,只包括这个包下的所有类,这个包下的子包不进行导入。
*package和import总结:
1、如果想将一个类放入包中,在这个类源文件第一句话写package
2、必须保证该类的class文件位于正确的目录下
(1)该类的源码可能会产生影响
(1)删除或转移这个源文件到另外的目录中
3、另外的类想访问的话:
(1)写全名,
(2)引入(import)
1、*
2、具体类名
4、访问位于同一个包中的类,不需要引入
5、必须class文件的最上层包的父目录位于classpath下
6、执行一个类需要写全包名

7、j2sdk中主要包的介绍:
*java.lang---包含一些java语言的核心类,如String、Math、Integer、System和Thread,提供常用的功能。这个类不用我们手动的去导入,编译器会自动帮我们导入的
*java.awt---包含了构成抽象窗口工具集(abstract window toolits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)
*java>applet---包含applet运行所需的一些类
*java.net---包含执行与网络相关的操作的类
*java.io---包含能提供多种输入/输出功能的类
*java.util---包含一些实用工具类,如定义系统特性,实用和日期日历相关的函数。

在命令窗口将一个文件夹下面的所有文件打包的命令:
jar -cvf xx.jar *.*
jar为打包命令符,-cvf 为命令符,xx..jar为要达成的jar文件名,*.*代表哪些文件要被打包,*.*代表当前目录下所有子目录和文件。使用时,我们要先通过命令窗口进入到要打包的当前目录中然后在执行上面的语句即可在当下目录下出现打包后的jar文件

那么如何使用打好的包文件呢,只要将包文件所在的目录(包含jar包文件)放到classpath当中那么就可以直接去调用这个包了。
11、类的继承
*Java中使用extends关键字实现类的继承机制,其语法规则为:
class [extends]{……}
*通过继承,子类自动拥有基类(父类superclass)的所有成员(成员变量和方法)
*java只支持单继承,不允许多继承:
*一个子类只能有一个基类(父类),一个基类(父类)可以派生出多个子类
12、访问控制
*java权限修饰符:public/ protected/ private 置于类的成员定义前,用来限定其他对象对该类对象成员的访问权限。
修饰符 类内部 同一个包 子类 任何地方
private Yes
default Yes Yes
protected Yes Yes Yes
public Yes Yes Yes Yes

*对于class的权限修饰只可以用public和default
*public类可以在任何地方被访问
*default类只能可以被同一个包内部的类访问
*如果父类中有private修饰的私有变量,那么子类继承父类的时候,虽然将这些私有成员变量给继承下来了,可是却不能访问它,也就是说,子类对父类的私有变量,具有占有权,但不具有使用权
13、方法的重写(override)
*在子类中如果不满足父类方法的实现,可以根据需要对基类(父类)中继承过来的方法进行重写。
*重写方法必须和被重写方法具有相同的方法名称、参数列表和返回类型
*重写方法不能使用比被重写方法更严格的访问权限
14、继承中的构造方法:
*子类的构造的过程中必须调用其基类的构造方法,(在子类构造方法中直接写super()即可,因为super就是父类对象的引用,所以super()相当于父类的构造方法)(因为子类继承父类后,子类就具有了父类的成员的所有权,那么如何实现子类对父类成员的所有权呢,系统通过创建子类对象的时候,在子类的对象中包含一个父类的对象,来实现对父类成员的调用,那么子类的对象的创建是通过子类的构造方法来实现的,而父类的对象当然也是通过父类的构造方法来实现,所以要想子类对象包含父类对象,就需要在子类的构造方法中调用父类构造方法)
*子类可以在自己的构造方法中使用super(argument_list)调用基类的构造方法
*使用this(argument_list)调用本类的另外的构造方法
*如果调用super,必须写在子类构造方法的第一行(先有父亲后有子女)
*如果子类的构造方法中没有显示的调用基类的构造方法,则系统默认调用基类的无参数的构造方法。
*如果子类构造方法中既没有显示调用基类构造方法,而基类中又没有无参的构造方法,则编译出错。
15、Object类
*Object类是所有java类的根基类
*如果在类的声明中未使用extends关键字,指明其基类,则,默认基类为Object类:
Public class Person{…….}
Public class Person extends Object{……}
*Object的toString()方法:
*Object类中定义有public String toString()方法,其返回值是String类型,描述当前对象的有关信息。
*在进行String与其它类型数据的连接操作时(如:System.out.println(“info”+person)),将自动调用该对象的toString()方法
*可以根据需要在用户自定义类型中重写toString()方法
*这个toString方法通常意义是用来在和字符串连接时,或者直接被打印出来时,返回一个代表这对象正常信息的一个字符串,而这个字符串怎么定义,由我们自己去定义,而默认的实现为:前面是类的名字然后是@后面是这个对象的hashcode。
*什么是哈希编码呢?简单的说hashcode就是一个对象的唯一标示,通过这个编码,可以找到和他对应的对象。

*Object类中的equals方法:
* A a = new A(); B b = new B(); a == b:比较的是对象a 和对象b的引用的地址是否相同,如果他们不是同一对象,那么这个比较在任何情况下都为false
*Object类中定义有:
* public Boolean equals(Object obj)方法
*提供定义对象是否“相等”的逻辑
*Object的equals方法定义为:x.equals(y)当x和y是同一个对象的引用时返回true否则返回false(),Object默认的equals实现和“==”是一个意思,都是比较对象的引用地址。
*j2sdk提供的一些类,如:String,Date等,重写了Object的equals方法,调用这些类的equals方法x.equals(y),当x和y所引用的对象是同一类对象且属性内容相等是(并不一定是相同对象),返回true否则返回false。
*可以根据需要在用户自定义类型中重写equals方法,如:
public class TestEquals {
public static void main(String[] args) {
Cat c1 = new Cat(1,2,3);
Cat c2 = new Cat(1,2,3);
System.out.println(c1 == c2);
System.out.println(c1.equals(c2));
}
}
class Cat{
int color;
int height, weight;

public Cat(int color, int height, int weight){
this.color = color;
this.height = height;
this.weight = weight;
}
public boolean equals(Object obj){
if(obj == null) return false;
else{
if(obj instanceof Cat){
Cat c = (Cat)obj;
if(c.color == this.color && c.height == this.height && c.weight == this.weight){
return true;
}
}
}
return false;
}
}

16、对象转型(casting)
*一个基类的引用类型变量可以“指向”其子类对象。
*一个基类的引用不可以访问其子类对象新增加的成员(属性和方法)。
*可以使用 引用 变量instanceof 类名 类判断该引用类型所“指向”的对象是否属于该类型或该类的子类。
*子类的对象可以当做基类的对象来使用称作向上转型(upcasting),反之称为向下转型(downcasting)向下转型又叫强制转型

17、动态绑定(多态)
*动态绑定(多态)指的是“在执行期间”(而非编译期)判断所引用对象的实际类型,根据其实际的类型调用其相应的方法
*多态形成有三个必要条件:
*要有继承
*要有方法的重写
*要有父类引用指向之类对象
*多态机制的解释:
在16对象转型中我们了解到父类的引用要是指向了子类的对象,那么父类的这个引用就不能调用子类中新增加的方法,只能调用它从父类中继承过来的成员;但是如果子类重写了父类的方法,这个重写的方法对父类的引用来说应该算是子类的一个新增的方法,按照第16调中规定的,父类引用不能调用子类新增的方法来说,那么这个重写的方法就不能被父类引用所调用,但是又由于,重写方法的返回值、参数列表和参数名称都和父类对象一致,编译器怎么去区分它是子类的一个新增方法呢,换句话来说,编译器调用这个方法时,到底是调用的父类的方法还是子类重写的方法呢?这时候编译器就是根据多态机制来判断到底调用的是哪个方法:在执行期间也就是程序已经编译好后,运行的时候,父类的引用会根据实际中,new的是父类的对象还是子类的对象来判断到底调用的是哪个方法,如果new的是父类,那么它调用的就是父类的方法,如果是new的子类那么调用的就是子类重写的方法,这个就叫做动态绑定就是在运行期间动态的选择所要调用的方法。我们在写主程序时,可以利用这个机制,尽量使用父类的引用,在调用这个方法时只要从这个父类继承,传递的时候传递一个子类,那么到时候调用的时候就可以调用到子类重写的方法了,而主程序就不用跟着子类对象的变化去更改程序了,这样极大的增加了程序的可扩展性。
18、抽象类
*用abstract关键字来修饰一个类时,这个类叫做抽象类;用abstract来修饰一个方法时,该方法叫做抽象方法
*含有抽象方法的类必须被声明为抽象类,抽象类必须被继承,抽象方法必须被重写。
*抽象类不能被实例化
*抽象方法只需声明,而不需实现。

19、Final关键字
*final的变量的值不能够被改变
*final的成员变量
*final的局部变量(形参)
*final的方法不能够被重写
*final的类不能够被继承
20、接口
*多个无关的类可以实现同一个接口。
*一个类可以实现多个无关的接口
*与继承关系类似,接口与实现类之间存在多态性。
*定义java类的语法格式为:
class [extends ]
[implements [,]*]{
*
}
*接口(interface)是抽象方法和常量值定义的集合。
*从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现。
*接口的特性
*接口可以多重实现;
*接口中声明的属性默认为public static final的;也只能是public static final的;
*接口中只能定义抽象方法,而这些方法默认为public的,也只能是public的;
*接口可以继承其他的接口,并添加新的属性和抽象方法。如果一个类实现了一个接口,而这个接口又从其他接口那里继承,那么这个类不仅要实现这个接口中的抽象方法,还要实现被继承的接口中的方法
*接口中方法写不写public默认的都是public的,成员常量写不写public static final默认的都是public static final的
*接口引用在指向实现这个接口的类的对象的时候,看到的只是这个对象中那些实现了接口中定义的成员,而这个对象的其他成员,接口引用时看不到的。
八、异常处理
*异常就是运行期出现的错误(出错后,观察错误的名字和行号最重要,要敢于去调试错误)
*Java异常是java提供的用于处理程序中错误的一种机制。
*所谓错误就是在程序运行的过程中发生的一些异常事件(如:除0益处,数组下标越界,所要读取的文件不存在)
*设计良好的程序应该在发生异常时提供处理这些错误的方法,使得程序不会因为异常的发生而阻断或产生不可预见的结果。
*java程序的执行过程中如出现异常事件,可以生成一个异常类对象,该异常对象封装了异常事件的信息,并将被提交给java运行时系统,这个过程称为抛出(throw)异常。
*当java运行时系统接收到异常对象时,会寻找有没有能处理这一异常的代码并把当前异常对象交给其处理,这一过程称为捕获(catch)异常。
*public void someMethod()
Throws SomeException { //声明该方法可能会抛出的异常
If (someCondition(){
Throw new SomeException(“错误原因”) // 构造并抛出异常对象
}
…………………………………………..
}

Try{ //调用该方法时试图捕获异常
someMethod();
} catch(SomeException e){
//异常处理代码;
}

*异常的分类
J2sdk中定义了很多异常类,这些类对应了各种各样可能出现的异常事件。


*Throwable是一个类,凡是继承了这个类的类对象都可以被抛出
*Error称为错误,由java虚拟机生成并抛出,包括动态链接失败,虚拟机错误等,程序对其不做处理
*Exception所有异常类的父类,其子类对应了各种各样可能出现的异常事件,一般需要用户显式的声明或捕获。
*RuntimeException一类特殊的异常,如被0除、数据下标越界等,其产生比较频繁,处理麻烦,如果显式的声明或捕获将会对程序的可读性和运行效率影响很大,因此由系统自动检测并将他们交给缺省的异常处理程序(用户可不必对其处理)(如路上有一个小石子,你开车过去的时候,要是非要将小石子给扫干净了才肯开车,那就太累了,所以没有必要对这些异常捕获,直接开过去就行了),这类exception不捕获可以成功通过编译,只是运行时才会报错
*还有一类除了RuntimeException的Exception类型,一般都是方法后面加了Throws xxxException 的这类,必须得去捕获它,这类exception必须被捕获否则编译的时候就通不过,更不用说在运行时了。

*异常的捕获和处理:
Try{
//可能抛出异常的语句
}catch(SomeException1 e){
………
}catch(SomeException2 e){
………
}finally{
………
}
上面格式的解释:
@ try代码段包含可能产生异常的代码
@try代码段后跟有一个或多个catch代码段
@每个catch代码段声明其能处理的一种特定类型的异常并提供处理的方法
@当异常发生时,程序会终止当前的流程,根据获取异常的类型去执行相应的catch代码段。
@finally段的代码无论是否发生异常都有执行。

*try语句:
*try{…}语句制定了一段代码,该段代码就是一次捕获并处理异常的范围。
*在执行过程中,该段代码可能会产生并报出一种或几种类型的异常对象,它后面的catch语句要分别对这些异常做相应的处理。
*如果没有异常发生,所有的catch代码都被略过不执行
*Catch语句:
*在catch语句块中是对异常进行处理的代码,每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不用类型的异常对象
*在catch中声明的异常对象(catch(SomeException e))封装了异常事件发生的信息,在catch语句块中可以使用这个对象的一些方法获取这个些信息。
*例如:
*getMessage( )方法,用来得到有关异常事件的信息
*printStackTrace( )方法,用来跟踪异常事件发生时执行堆栈的内容。
*Finally语句
*finally语句为异常处理提供统一的出口,使得在控制流程转到程序的其他部分以前,能够对程序的状态做统一的管理。
*无论try所指定的程序块中是否抛出异常,finally所指定的代码都会被执行。
*通常finally语句中可以进行资源的清除工作,如:
*关闭打开的文件
*删除临时文件
*……
*如果某种异常自己处理不了那么就在方法声明的时候将它抛出去,如:

直到抛到能处理它的地方为止,如:

*一般的异常都是java虚拟机自动给抛出的,如runtimeexception这类异常是不会自动抛出的,这时候就需要我们手动抛出如:


*注:在写异常捕获时,在一个try语句块中,基类异常的捕获语句不可以写在子类异常捕获语句的上面,即要是一个try后面跟了多个catch时,catch括号后面的异常范围必须是从小到大排列下来的。如:


*使用自定义异常:
* 通过继承java.lang.Exception类声明自己的异常类。
* 再方法适当的位置生成自定义异常的实例,并用throw语句抛出。
* 在方法的声明部分用throws语句声明该方法可能抛出的具体异常
如:


*重写方法需要抛出与原来方法所抛出异常类型一致的异常或者不抛出异常。如:

*常见异常类:
IOException:输入输出流异常
FileNotFoundException:所找文件不存在异常
Exception:异常的总类
ArrayIndexOutOfBoundsException:数组越界异常
NullPointerException:空指针异常
ArithmeticException:数学运算异常,如除0
NumberFormatException:数字格式有问题异常
九、数组
*数组可以看成是多个相同类型数据组合,对这些数据的统一管理
*数组变量属于引用类型,数组可以看成是对象,数组中的每个元素相当于该对象的成员变量。
*数组中的元素可以是任何数据类型,包括基本类型和引用类型
*一维数组的声明方式:
type var[ ]; 或 type[ ] var
如:int a1[ ] 、 double b [ ] 、 Person [ ] p1 、 String s1 [ ]
*java语言中声明数组时,不能指定其长度(数组中元素的个数)例如:int a [ 5]这个是错误的。
*java中使用关键字new创建一个数组对象,格式为:
数组名 = new 数组元素的类型[数组的个数]例如:

*元素为引用数据类型的数组(元素为引用数据类型的数组中的每一个元素都需要实例化)

*数组的初始化:(分为动态初始化和静态初始化)
*动态初始化:数组定义时数组元素分配空间(定义数据,如new一个具有确定长度的数组就会在堆内存中分配好确定长度的空间:int a[] a = new a[5];就在内存中分配5个等值大小的空间)和赋值(给具体的数据元素赋值)的操作分开进行如:

*静态初始化:在定义数组的同时就为数组元素分配空间并赋值如:

*数组默认初始化:数组是引用类型,它的元素相当于类的成员变量,因此数组分配空间后,每个元素也被按照成员变量的规则被隐式初始化了如:

*数组元素的引用:
*定义并用运算符new为之分配空间后,才可以引用数组中的每个元素,数组元素的引用方式为:
*arrayName[index]
*index为数组元素下标,可以是整形常量或者整型表达式。如:
a[3]、 b[i], c[6*i]
*数组元素小标从0开始;长度为n的数据的合法下标取值范围为:0~n-1
*每个数据都有一个属性length,指明它的长度,例如:
*a.length的值为数据a的长度(元素的个数)


*将字符串类型的数据转化成各种基础类型的数据的时候,可以用各个基础类型的包装类如Integer、Long、Byte它们各自都有一个方法可以将字符串转化为各自的类型即:
Integer.parseInt 、 Long.parseLong 、Byte.parseByte,这些方法都是静态的,可以直接用类名.方法名的形式来调用,将字符串转换为相应的类型
*整数类型的数组排序(后面有三幅程序片段图,它们的运算效率是一次递增的):



*元素是引用型变量的数据排序(冒泡排序):


游戏(几个人围一圈,数到三的推出):


*二维数组
*二维数组可以看成以数组为元素的数组,例如:
Int a [] [] = {{1,2},{3,4,5,6},{7,8,9}}
*java 中多维数组的声明和初始化应按从高维到低维的顺序进行,例如:
Int a[] [] = new int [3] [];
a[0] = new int [2];
a[2] = new int [4];
a[3] = new int [3];
int t1 [][] = new int [][4]; //非法,不合理


二维数组的使用实例:







十、java常用类:

*String字符串(一种不可变的字符串)







*StringBuffer(可变字符串)
什么叫可变字符串:就是一个字符串在它原来分配的内存空间内的长度可以自动变化,不受限制那么这种就叫可变的字符串,String类型为不可变的字符串,一旦内存中有对象了,那么他的长度也就固定了如:String s1 String s2 s1和s2是两个字符串对象他们的长度固定不变:
s1+=s2;这个反应在内存中就是,将s1所指的内存中的字符串和s2所指内存的字符串都取出来,然后合并为一个长的字符串,重新在内存中分配一个可以存放新字符串的空间,然后将新分配的空间的地址付给s1,所以s1+=s2并不是一种可变字符串的表示,因为s1所指的内存发生变化,不是原先的内存,没有在原先内存中增加字符串,所以它体现的还是不可变的字符串,所以String是一种不可变的字符串,而StringBuffer才是一种可变的字符串:

















十一、容器
*所谓容器就是装其他各种各样对象的一种“器皿”
(数组也是一种容器,但是数组的长度一旦确定就不能在更改了,跟不可变的字符串String一样,只能new一个更大的数组然后将那个数组arrayCopy到这个大的数组中来)


什么叫可重复呢?容器中装的都是对象的引用,对象的引用除非是同一个对象,否则是不会重复的,在容器这一章内部是这样规定重复的概念的,就是指两个引用所指对象可以相互的equals那就算重复。
*Map接口定义了存储“键(key)--- 值(value)映射对”的方法。





*Iterator接口,有三个方法,next、hasNext、remove,每一个集合都从collection接口中继承了一个方法,就是iterator这个方法,它返回一个实现了Iterator接口的对象,这个对象具有next、hasNext、remove这三个方法,说明了每一个集合都实现了Iterator这个接口,同时又实现了collection的iterator方法,所以每一个集合类通过它实现的iterator方法返回一个实现了Iterator接口的对象(即它本身对象),通过这个三个方法来遍历这个集合中的每一个元素。
Iterator接口中的remove方法是在遍历集合时删除元素的唯一安全的方法,当使用iterator来遍历一个集合时,Iterator接口将对这个集合进行锁定,致使遍历时要想删除集合中的元素必须使用Iterator接口中的remove方法,而不能使用集合类本身的remove方法。




















*泛型(凡是用到集合的尽量使用泛型)






十二、IO(流)
流大体分为两类,输入流和输出流,所谓的输入和输出是指站在程序的角度,如果从文件中向程序中读取数据的时候,称为输入流,从程序向文件输出数据的时候,就称为输出流。
*根据流的方向,我们将流分为输入流和输出流
*根据处理数据单位的不同可分为字节流(InputStream/OutputStream)和字符流(Reader/Writer)
*根据实现的功能不同分为节点流(直接连接数据源的流)和处理流(套在其他的流之上的流)
* InputStream/OutputStream/ Reader/Writer都是抽象类分别是字节流和字符流























转换流:








上面程序的显示结果为:


显示结果为:



十三、多线程


线程是一个程序里面不同的执行路径。
进程是一个静态的概念,进程是0个或多个线程的集合。

两种方法,一种是实现Runable 接口的,一种是从Thread类继承,通常第一个种方法要比第二种好一些,因为如果实现了Thread类那么就不能继承其他的类了,所以通过实现接口还是比较灵活的一种选择方式,所以在能选择使用接口实现的线程的时候,尽量不要使用继承Thread来实现多线程。

线程的状态:

线程状态的控制:







线程同步问题:
什么叫线程同步?线程同步就是:对访问同一份资源的多个线程之间,来进行协调的工作


也可以写成这样子:


死锁问题:
什么是死锁问题呢?死锁问题就是指,在线程同步的时候,有多个线程去访问两个资源,比如a资源和b资源,如果线程1访问了a资源后,锁定了a资源,只要它在访问b资源,那么就能将程序执行完毕,而线程2在访问了b资源后,锁定了b资源,a资源无法访问b资源,此时只要线程2再访问a资源,那么就能将程序执行完毕,这时线程1占着a资源,线程2占着b资源,相互锁定,此时就会出现死锁现象,例如下面的程序:

解决死锁的方法可以将锁定的颗粒范围放大一些,不要将一个资源分成两部分来锁,这样很容易出现死锁现象.

也就是说,一个类中有多个方法,这些方法有的有synchronaized,有的没有,也就说说有的写了同步的处理,有的没有写同步的处理,那么这个时候一旦有一个线程访问这个类对象中的某个被synchronaize修饰的方法时,那么这个对象中的所有被synchronaized修饰的方法都被锁住,不能被其他线程所访问,但是没有被synchronized修饰的却,能被其他线程所访问,所以在考虑线程同步问题的时候,要考虑详细,那些方法需要同步,哪些不需要同步,写了同步的方法,执行效率必然会降低,没写的却有可能出现线程不同步问题.所以要慎重处理.

比如有一个访问数据库的类,这个类中有两个方法对数据库进行操作,一个是从数据库中读取数据,一个是从数据库重修改数据,那么我们应该给哪个方法加synchronized?当然是改的那个方法,因为它涉及到数据的修改,而读取数据的方法则不用加.也就是说如果两个方法改了同一个值,那么这两个方法都应该加同步.

这里有一个很经验的消费者很生产者的程序:




这个程序中涉及两个方法:现在介绍:

wait
public final void wait()
throws InterruptedException
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。换句话说,此方法的行为就好像它仅执行 wait(0) 调用一样。
当前的线程必须拥有此对象监视器。该线程发布对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得对监视器的所有权后才能继续执行。
对于某一个参数的版本,实现中断和虚假唤醒是可能的,而且此方法应始终在循环中使用:
synchronized (obj) {
while ()
obj.wait();
... // Perform action appropriate to condition
}

此方法只应由作为此对象监视器的所有者的线程来调用。请参阅 notify 方法,了解线程能够成为监视器所有者的方法的描述。
抛出:
IllegalMonitorStateException - 如果当前的线程不是此对象监视器的所有者。
InterruptedException - 如果在当前线程等待通知之前或者正在等待通知时,另一个线程中断了当前线程。在抛出此异常时,当前线程的中断状态 被清除。
notify
public final void notify()
唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。
直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。
此方法只应由作为此对象监视器的所有者的线程来调用。通过以下三种方法之一,线程可以成为此对象监视器的所有者:
• 通过执行此对象的同步 (Sychronized) 实例方法。
• 通过执行在此对象上进行同步的 synchronized 语句的正文。
• 对于 Class 类型的对象,可以通过执行该类的同步静态方法。
一次只能有一个线程拥有对象的监视器。
抛出:
IllegalMonitorStateException - 如果当前的线程不是此对象监视器的所有者。
notifyAll
public final void notifyAll()
唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个 wait 方法,在对象的监视器上等待。
直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。
此方法只应由作为此对象监视器的所有者的线程来调用。请参阅 notify 方法,了解线程能够成为监视器所有者的方法的描述。
抛出:
IllegalMonitorStateException - 如果当前的线程不是此对象监视器的所有者。




十四、网络编程









只有TCP才分服务器端(server)和客户端(client),UDP是不分

端口号: 是用来区分一台机器上不同的应用程序的,通过ip地址可以找到网络中的某一台主机,那么通过端口号就能找到这台主机上的具体的某一个应用程序。端口号在计算机中用两个字节来表示,所以最多只能有65536个端口号。如果自己要写程序的端口最好用1024以上的,因为1024一下的操作系统可能会随时征用。http协议默认的端口是80、ftp协议默认的端口是21,smtp(简单邮件的发送协议)的端口为25

端口号本身分为两种:一种是TCP端口,UDP端口,每一种端口都有65536个

服务器端:

客户端:


Socket编程要同时运行两个程序,才能实现客户端到服务器端的连接

一旦客户端和服务器端建立通道,连接成功后,那么就可以通过输入输出流来彼此沟通了:


上面这两种socket编程会有问题,尤其是服务器端的编程,在接收客户端通过流写过来的数据的时候,通过一个叫readUTF()的方法来接收客户端写过来的信息,但是此方法为阻塞式的方法,如果客户端一直不往服务器端写东西,那么此方法就一直阻塞在那里,那么其他客户端想连接这个服务器端就会连接不上,因为只有等下一次循环执行到方法accept的时候才能连接上另外一个客户端,所以在建立客户端连接的死循环中处理客户端过来的数据是会有问题的!

下面看几对关于socket编程的程序:
第一对程序:服务器端向客户端写数据,客户端从服务器端接收数据
服务器端:
import java.net.*;
import java.io.*;
public class TestServer {
public static void main(String[] args) {
try{
ServerSocket ss = new ServerSocket(8888);
while(true) {
Socket s = ss.accept();
OutputStream os = s.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF("Hello,"+s.getInetAddress()+ "port#"+s.getPort()+ " bye-bye!");
dos.flush();
dos.close();
}
}catch(IOException e){
e.printStackTrace();
System.out.println("程序运行错误"+e);
}
}
}
客户端:
import java.net.*;
import java.io.*;
public class TestClient {
public static void main(String[] args){
try{
Socket s = new Socket("127.0.0.1",8888);
InputStream is = s.getInputStream();
DataInputStream dis = new DataInputStream(is);
System.out.println(dis.readUTF());

dis.close();
s.close();
}catch(ConnectException c){
c.printStackTrace();
System.out.println("服务器连接失败");
}catch(IOException i){
i.printStackTrace();

}
}
}


第二对程序:服务器端和客户端相互读取数据
服务器端:
import java.net.*;
import java.io.*;
public class TestSocketServer {
public static void main(String[] args) {
InputStream in = null;
OutputStream out = null;

try{
ServerSocket ss = new ServerSocket(5888);
Socket s1 = ss.accept();
in = s1.getInputStream();
out = s1.getOutputStream();
DataInputStream dis = new DataInputStream(in);
DataOutputStream dos = new DataOutputStream(out);
String s = null;
if((s = dis.readUTF()) != null){
System.out.println(s);
System.out.println("from:" + s1.getInetAddress());
System.out.println("port:" + s1.getPort());
}
dos.writeUTF("hi, hello");
dis.close();
dos.close();
s1.close();
}catch(IOException e){
e.printStackTrace();
}
}
}

客户端:
import java.net.*;
import java.io.*;
public class TestSocketClient {
static InputStream in = null;
static OutputStream out = null;
public static void main(String[] args){
try{
Socket socket = new Socket("localhost", 5888);
in = socket.getInputStream();
out = socket.getOutputStream();
DataInputStream dis = new DataInputStream(in);
DataOutputStream dos = new DataOutputStream(out);
dos.writeUTF("hey!");
dos.flush();
String s = null;
if((s = dis.readUTF()) != null) {
System.out.println(s);
}
dis.close();
dos.close();
}catch(ConnectException e){
e.printStackTrace();
}catch(IOException c){
c.printStackTrace();
}
}
}

第三个程序:
服务器端:
import java.net.*;
import java.io.*;
public class QQserver {

public static void main(String[] args){

try {
ServerSocket ss = null;

try{
ss = new ServerSocket(4700);
}catch(IOException i){
System.out.println("连接不成功!!!");
i.printStackTrace();
System.exit(0);
}
Socket s = null;

try{
s = ss.accept();
}catch(IOException o){
o.printStackTrace();
}

BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintStream ps = new PrintStream(s.getOutputStream());
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String line = "";
System.out.println("client:"+br.readLine());
line = in.readLine();
while(!line.equals("bye")){
ps.println(line);
ps.flush();
System.out.println("server:"+line);
System.out.println("client:"+br.readLine());
line = in.readLine();
}
br.close();
ps.close();
s.close();
ss.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
客户端:
import java.net.*;
import java.io.*;
public class QQClient {
public static void main(String[] args){
try{
Socket s = new Socket("127.0.0.1",4700);

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedReader bd = new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintWriter pw = new PrintWriter(s.getOutputStream());

String line = br.readLine();

while(!line.equals("bye")){
System.out.println("client:"+line);
pw.write(line);
pw.flush();
System.out.println("server:"+bd.readLine());

line = br.readLine();
}

bd.close();
br.close();
s.close();
}catch(IOException i){
i.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}
}
}


UDP:(数据报,将数据打成一个个小包进行传输)对于UDP,严格意义上来说没有服务器端和客户端的概念,因为他不建立连接,不需要区分什么服务器端的socket和客户端的socket,而对于UDP来说,它是一种无线的连接(tcp为有线连接,需要先建立连接,建立传出通道,这种发送形式比较可靠,发过去的数据都能接收到)它不确保数据的能一定被收到,它也有自己的socket,这个socket的名字为DatagramSocket(此类表示用来发送和接收数据报包的套接字。每个在数据报套接字上发送或接收的包都是单独编址和路由的。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。)

参考TestUDPServer.java

参考TestUDPServer.java


类 DatagramPacket此类表示数据报包。
数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。
如果是将一个long类型数打成包从客户端发送到服务器端呢?同时如何从客户端把一个long类型的数打成包发送到服务器端呢

这其中涉及到了如何把一个long类型的数转化成为一个字节数组(没有直接可用的方法,其方法可采用io流的形式实现此功能,具体如何实现可参考下面两个程序):


总结:

TCP和UDP的区别是:
TCP:速度比较慢,但是很可靠
UDP:速度快,但是不可靠
TCP/UDP的写法特别固定,记住一次就够了


十五、GUI编程


所有的可以显示出来的图形元素都叫做component
Component分为两大类,一种为组件,一种为容器;
组件有:
Buttion:按钮
TextArea: 输入框
Label:静态文本
TextField:输入域
List: 下拉列表
……
容器:Container翻译过来就是容器的意思,它是用来容纳那些可以显示出来的component的,比如窗口,容纳了其他可以操作的菜单、按钮(这些都是可以显示的component)
Container中可以容纳component,Container中当然也可以容纳Container了(容器中套容器,因为Container本身就是一种component)。
Container又分为Window与Panel
Window:可以独立显示出来作为一个应用程序的窗口称为Window
Panel:也可以容纳其他的元素,一般的是不可见的,它不能作为应用程序的独立窗口显示出来,要想显示只能将panel装载在window中进行显示,它既是一种容器,又是一种组件,用于为其他控件提供可识别的分组,通常使用面板按功能细分窗口
Window本身又分为两种:一种是Frame;一种是Dialog。
Window:就是指应用程序外层的大窗口,
Dialog:指的是弹出窗口,它有个特点,如果不点掉它,后面的程序是操作不了的





十六、专题讲座:
日期处理

 很多时候,我们需要对日期的格式进行处理,将其展示成我们自己心中想要的格式,比如从数据库中查出一个日期类型的数据,如果直接展示出来它的格式会是类似于1987-02-05这种的,那么我们如何去格式化呢,在没有老师或者书本存在的情况下我们如何了解这部分知识呢,所以我们应该学会自己去寻找知识,那么如何寻找呢,按照正常的人的思维,我们首先应该去查询JDK的api文档,看看它上面给的类中有没有什么好用的方法,如果找不到,在看看它的父类中有没有好用的方法,实在找不出来我们就需要通过google去寻找答案,查看网上的信息,最后根据网上给的资料,再结合api文档,进行相关知识的了解。
说一千道一万,到底如何转化日期的格式呢?具体作法如下:
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy年MM月dd日”);
Sdf.format(date);
即可。
 如上方法是格式化一个日期的时候用的,那么如果我要单独的取出格式化日期中的具体的某个年份、月份或者日子,这时候怎么实现呢。实现方法如下:
Date date = new Date()
Calendar c = Calendar.getInstance();
c.setTime(date);
System.out.println(c.get(Calendar.Month))
 通过ResultSet的getDate()方法得到的date类型只包括日期本身即年月份,不包括时间即时分秒,
因为通过ResultSet的getDate()方法得到的date类型是java.sql.Date这种类型只包括日期,不包括时间,而java.util.Date则既包括上面的java.sql.Date(日期)也包括java.sql.Time即时间,同时还包括java.sql.Timestamp:

如上图所示,java.util.Date类的三个子类分别是Date, Time,Timestamp;它们的类型分别是java.sql.Date(只包含日期) java.sql.Time(只包含时间)java.sql.Timestamp(时间戳,既包含时间又包含日期,专用于数据库的日期类型),这三个类都是用来跟数据库打交道的,它们都是java.util.Date的子类,所以也可以通过SimpleDateForm来进行格式化,例如:
ResultSet rs = statement. executeQuery(sql); //从数据库获得sql语句查出的结果集
Timestamp tt = rs.getTimeStamp();
SimpleDateForm sdf = new SimleDateForm(“yyyy-MM-dd HH:mm:ss”);
System.out.println(sdf.format(tt));

 日期和时间总结:
1、 什么是UTC/UT?
UTC指的是按着某一个地方的原子钟来计算的一个世界时间(在网上查资料),即所谓的世界标准时间,UT是对UTC的一个科学上的简称。
2、 什么是GMT?
即所谓的格林威治时间,跟UTC指的是一回事,也就是所谓的世界标准时间。
3、 怎么获得系统当前时间?
方法很多种,我在这里一一列举出来:

可以通过api文档查看这些类的相应方法,可以获得系统的当前时间。
简单的说一下上面几个类之间的关系,我们通过一个连接图来表示:

具体的如何获取系统当前时间:
(1) System. currentTimeMillis()返回当前时间与协调世界时 1970 年 1 月 1 日午夜之间的时间差(以毫秒为单位测量)。返回值为long类型,这个方法一般用来计算某个程序执行了多长时间:在程序执行开始的时候可以通过记录下开始时间,在程序执行结束的时候可以通过记录下结束的时间,这样两个时间相减就是获得了这个程序的执行时间。
(2) java.util.Date date = new java.util.Date();
(3) Calendar c = Calendar. getInstance(),然后通过c的方法获得系统当前时间的各个元素

4、 如何格式化一个时间?
参加上面的日期格式化内容,通常通过SimpleDateForm这个类来格式化日期
5、 如何把一个字符串转化成一个date或者time类型?
使用java.sql.Timestamp类的静态方法:public static Timestamp valueOf(String s),但是此字符串必须满足如下格式:yyyy-mm-dd hh:mm:ss.fffffffff,这样就可以将字符串转换成一个date类型了。。
6、如何处理数据库中的date或time类型?
处理数据库中的时间参见上面的日期格式处理即可,即就是从数据库中取出日期或者时间或者时间和日期,由于他们是java.util.Date的子类,所以可以通过simpleDateFormat类来进行格式化,
我们这里想重点讲一下时区的问题:比如我想得到日本的时候怎么得到呢?大家都知道格林威治时间时间表示的是世界标准时间,那么GregorianCalendar是Calendar的子类它则表示的是世界时间的日历,那么我们可以通过它获得日本时间。怎么做呢?如下所示:
Calendar cJapan = new GregorianCalendar(TimeZone.getTimeZone(“Japan”));
System.out.println(cJapan.get(Calendar.HOUR_OF_DAY));
在这里我们使用的方法其实是:GregorianCalendar的构造方法:public GregorianCalendar(TimeZone zone),通过指定想要得到日期所在地的时区就可以得到这个地区的时间了,那么如何获得这个地区的时区呢?我们也是通过类:TimeZone的一个静态方法获得的,public static TimeZone getTimeZone(String ID),其中id是指的时区的id,我们可以通过:
For(String str: TimeZone.getAvailableIDs()){
System.out.println(str);
}
来打印出世界上所有的时区id,从而在其中找到我们想要的时区id,最后获得这个时区的时间即可。


后期补充知识:
一/oracle 中的blob类型和clob类型并给出具体用法及示例
数据库中提供了两种字段类型 Blob 和 Clob 用于存储大型字符串或二进制数据(如图片)。

Blob 采用单字节存储,适合保存二进制数据,如图片文件。
Clob 采用多字节存储,适合保存大型文本数据。

Oracle中处理BLOB/CLOB字段的方式比较特别,所以需要特别注意下面两点:

1. 在Oracle JDBC中采用流机制对 BLOB/CLOB 进行读写操作,所以要注意不能在批处理中读写 BLOB/CLOB字段,否则将出现
Stream type cannot be used in batching 异常。

2. Oracle BLOB/CLOB 字段本身拥有一个游标(cursor),JDBC通过游标对Blob/Clob字段进行操作,在Blob/Clob字段创建之前,无法获取其游标句柄,会出现
Connection reset by peer: socket write error 异常。

正确的做法是:首先创建一个空 Blob/Clob 字段,再从这个空 Blob/Clob字段获取游标,例如下面的代码:

PreparedStatement ps = conn.prepareStatement( " insert into PICTURE(image,resume) values(?,?) " );
// 通过oralce.sql.BLOB/CLOB.empty_lob()构造空Blob/Clob对象
ps.setBlob( 1 ,oracle.sql.BLOB.empty_lob());
ps.setClob( 2 ,oracle.sql.CLOB.empty_lob());

ps.excuteUpdate();
ps.close();

// 再次对读出Blob/Clob句柄
ps = conn.prepareStatement( " select image,resume from PICTURE where id=? for update " );
ps.setInt( 1 , 100 );

ResultSet rs = ps.executeQuery();
rs.next();

oracle.sql.BLOB imgBlob = (oracle.sql.BLOB)rs.getBlob( 1 );
oracle.sql.CLOB resClob = (oracle.sql.CLOB)rs.getClob( 2 );

// 将二进制数据写入Blob
FileInputStream inStream = new FileInputStream( " c://image.jpg " );
OutputStream outStream = imgBlob.getBinaryOutputStream();

byte [] buf = new byte [ 10240 ];
int len;
while (len = inStream.read(buf) > 0 ) {
outStream.write(buf, 0 ,len);
}
inStream.close();
outStream.cloese();

// 将字符串写入Clob
resClob.putString( 1 , " this is a clob " );

// 再将Blob/Clob字段更新到数据库
ps = conn.prepareStatement( " update PICTURE set image=? and resume=? where id=? " );
ps.setBlob( 1 ,imgBlob);
ps.setClob( 2 ,resClob);
ps.setInt( 3 , 100 );

ps.executeUpdate();
ps.close();参考资料:开发者在线http://www.builder.com.cn/files/list-0-0-50259-1-1.htm
环境:
Database: Oracle 9i
App Server: BEA Weblogic 8.14
表结构:
CREATE TABLE TESTBLOB (ID Int, NAME Varchar2(20), BLOBATTR Blob)
CREATE TABLE TESTBLOB (ID Int, NAME Varchar2(20), CLOBATTR Clob)
JAVA可以通过JDBC,也可以通过JNDI访问并操作数据库,这两种方式的具体操作存在着一些差异,由于通过App Server的数据库连接池JNDI获得的数据库连接提供的java.sql.Blob和java.sql.Clob实现类与JDBC方式提供的不同,因此在入库操作的时候需要分别对待;出库操作没有这种差异,因此不用单独对待。

一、BLOB操作
1、入库
(1)JDBC方式
//通过JDBC获得数据库连接
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection con = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:testdb", "test", "test");
con.setAutoCommit(false);
Statement st = con.createStatement();
//插入一个空对象empty_blob()
st.executeUpdate("insert into TESTBLOB (ID, NAME, BLOBATTR) values (1, "thename", empty_blob())");
//锁定数据行进行更新,注意“for update”语句
ResultSet rs = st.executeQuery("select BLOBATTR from TESTBLOB where ID=1 for update");
if (rs.next())
{
//得到java.sql.Blob对象后强制转换为oracle.sql.BLOB
oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob("BLOBATTR");
OutputStream outStream = blob.getBinaryOutputStream();
//data是传入的byte数组,定义:byte[] data
outStream.write(data, 0, data.length);
}
outStream.flush();
outStream.close();
con.commit();
con.close();
(2)JNDI方式
//通过JNDI获得数据库连接
Context context = new InitialContext();
ds = (DataSource) context.lookup("ORA_JNDI");
Connection con = ds.getConnection();
con.setAutoCommit(false);
Statement st = con.createStatement();
//插入一个空对象empty_blob()
st.executeUpdate("insert into TESTBLOB (ID, NAME, BLOBATTR) values (1, "thename", empty_blob())");
//锁定数据行进行更新,注意“for update”语句
ResultSet rs = st.executeQuery("select BLOBATTR from TESTBLOB where ID=1 for update");
if (rs.next())
{
//得到java.sql.Blob对象后强制转换为weblogic.jdbc.vendor.oracle.OracleThinBlob(不同的App Server对应的可能会不同)
weblogic.jdbc.vendor.oracle.OracleThinBlob blob = (weblogic.jdbc.vendor.oracle.OracleThinBlob) rs.getBlob("BLOBATTR");
OutputStream outStream = blob.getBinaryOutputStream();
//data是传入的byte数组,定义:byte[] data
outStream.write(data, 0, data.length);
}
outStream.flush();
outStream.close();
con.commit();
con.close();
2、出库
//获得数据库连接
Connection con = ConnectionFactory.getConnection();
con.setAutoCommit(false);
Statement st = con.createStatement();
//不需要“for update”
ResultSet rs = st.executeQuery("select BLOBATTR from TESTBLOB where ID=1");
if (rs.next())
{
java.sql.Blob blob = rs.getBlob("BLOBATTR");
InputStream inStream = blob.getBinaryStream();
//data是读出并需要返回的数据,类型是byte[]
data = new byte[input.available()];
inStream.read(data);
inStream.close();
}
inStream.close();
con.commit();
con.close();
二、CLOB操作
1、入库
(1)JDBC方式
//通过JDBC获得数据库连接
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection con = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:testdb", "test", "test");
con.setAutoCommit(false);
Statement st = con.createStatement();
//插入一个空对象empty_clob()
st.executeUpdate("insert into TESTCLOB (ID, NAME, CLOBATTR) values (1, "thename", empty_clob())");
//锁定数据行进行更新,注意“for update”语句
ResultSet rs = st.executeQuery("select CLOBATTR from TESTCLOB where ID=1 for update");
if (rs.next())
{
//得到java.sql.Clob对象后强制转换为oracle.sql.CLOB
oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob("CLOBATTR");
Writer outStream = clob.getCharacterOutputStream();
//data是传入的字符串,定义:String data
char[] c = data.toCharArray();
outStream.write(c, 0, c.length);
}
outStream.flush();
outStream.close();
con.commit();
con.close();
(2)JNDI方式
//通过JNDI获得数据库连接
Context context = new InitialContext();
ds = (DataSource) context.lookup("ORA_JNDI");
Connection con = ds.getConnection();
con.setAutoCommit(false);
Statement st = con.createStatement();
//插入一个空对象empty_clob()
st.executeUpdate("insert into TESTCLOB (ID, NAME, CLOBATTR) values (1, "thename", empty_clob())");
//锁定数据行进行更新,注意“for update”语句
ResultSet rs = st.executeQuery("select CLOBATTR from TESTCLOB where ID=1 for update");
if (rs.next())
{
//得到java.sql.Clob对象后强制转换为weblogic.jdbc.vendor.oracle.OracleThinClob(不同的App Server对应的可能会不同)
weblogic.jdbc.vendor.oracle.OracleThinClob clob = (weblogic.jdbc.vendor.oracle.OracleThinClob) rs.getClob("CLOBATTR");
Writer outStream = clob.getCharacterOutputStream();
//data是传入的字符串,定义:String data
char[] c = data.toCharArray();
outStream.write(c, 0, c.length);
}
outStream.flush();
outStream.close();
con.commit();
con.close();
2、出库
//获得数据库连接
Connection con = ConnectionFactory.getConnection();
con.setAutoCommit(false);
Statement st = con.createStatement();
//不需要“for update”
ResultSet rs = st.executeQuery("select CLOBATTR from TESTCLOB where ID=1");
if (rs.next())
{
java.sql.Clob clob = rs.getClob("CLOBATTR");
Reader inStream = clob.getCharacterStream();
char[] c = new char[(int) clob.length()];
inStream.read(c);
//data是读出并需要返回的数据,类型是String
data = new String(c);
inStream.close();
}
inStream.close();
con.commit();
con.close();
需要注意的地方:
1、java.sql.Blob、oracle.sql.BLOB、weblogic.jdbc.vendor.oracle.OracleThinBlob几种类型的区别
2、java.sql.Clob、oracle.sql.CLOB、weblogic.jdbc.vendor.oracle.OracleThinClob几种类型的区别
Blob是指二进制大对象也就是英文Binary Large Object的所写,而Clob是指大字符对象也就是英文Character Large Object的所写。由此可见这辆个类型都是用来存储大量数据而设计的,其中BLOB是用来存储大量二进制数据的;CLOB用来存储大量文本数据。
那么有人肯定要问既然已经有VARCHAR和VARBINARY两中类型,为什么还要再使用另外的两种类型呢?其实问题很简单,VARCHAR和VARBINARY两种类型是有自己的局限性的。首先说这两种类型的长度还是有限的不可以超过一定的限额,以VARCHAR再ORA中为例长度不可以超过4000;那么有人又要问了,LONGVARCHAR类型作为数据库中的一种存储字符的类型可以满足要求,存储很长的字符,那为什么非要出现CLOB类型呢?其实如果你用过LONGVARCHAR类型就不难发现,该类型的一个重要缺陷就是不可以使用LIKE这样的条件检索。(稍候将介绍在CLOB中如何实现类似LIKE的模糊查找)另外除了上述的问题外,还又一个问题,就是在数据库中VARCHAR和VARBINARY的存取是将全部内容从全部读取或写入,对于100K或者说更大数据来说这样的读写方式,远不如用流进行读写来得更现实一些。
在JDBC中有两个接口对应数据库中的BLOB和CLOB类型,java.sql.Blob和java.sql.Clob。和你平常使用数据库一样你可以直接通过ResultSet.getBlob()方法来获取该接口的对象。与平时的查找唯一不同的是得到Blob或Clob的对象后,我们并没有得到任何数据,但是我们可以这两个接口中的方法得到数据
例如:
Blob b=resultSet.getBlob(1);
InputStream bin=b.getBinaryStryeam();
Clob c=resultSet.getClob(2);
Reader cReader=c.getCharacterStream():
关于Clob类型的读取可以使用更直接的方法,就是直接通过ResultSet.getCharacterStream();方法获得字符流,但该方法并不安全,所以建议还是使用上面例子的方法获取Reader。
另外还有一种获取方法,不使用数据流,而是使用数据块。
例如
Blob b=resultSet.getBlob(1);
byte data=b.getByte(0,b.length());
Clob c=resultSet.getClob(2);
String str=c.getSubString(0,c.length()):
在这里我要说明一下,这个方法其实并不安全,如果你很细心的话,那很容易就能发现getByte()和getSubString()两个方法中的第二个参数都是int类型的,而BLOB和CLOB是用来存储大量数据的。而且Bolb.length()和Clob.length()的返回值都是long类型的,所以很不安全。这里不建议使用。但为什么要在这里提到这个方法呢?稍候告诉你答案,这里你需要记住使用数据块是一种方法。

在存储的时候也同样的在PreparedStatement和CallableStatememt中,以参数的形式使用setBlob()和setClob方法把Blob和Clob对象作为参数传递给SQL。这听起来似乎很简单对吧,但是并非我们想象的这样,很不幸由于这两个类型的特殊,JDBC并没有提供独立于数据库驱动的Blob和Clob建立对象。因此需要自己编写与驱动有关的代码,但这样又牵掣到移植性。怎样才是解决办法呢?这就要用到前面说过的思想了使用数据块进行写操作。同样用PreparedStatement和CallableStatememt类,但参数的设置可以换为setAsciiStream、setBinaryStream、setCharacterStream、setObject(当然前3个同样存在长度的问题)
下面给大家个例子以方便大家理解
public void insertFile(File f) throws Exception{
FileInputStream fis=new FileInputStream(f,Connection conn);
byte[] buffer=new byte[1024];
data=null;
int sept=0;int len=0;

while((sept=fis.read(buffer))!=-1){
if(data==null){
len=sept;
data=buffer;
}else{
byte[] temp;
int tempLength;

tempLength=len+sept;
temp=new byte[tempLength];
System.arraycopy(data,0,temp,0,len);
System.arraycopy(buffer,0,temp,len,sept);
data=temp;
len=tempLength;
}
if(len!=data.length()){
byte temp=new byte[len];
System.arraycopy(data,0,temp,0,len);
data=temp;
}
}
String sql="insert into fileData (filename,blobData) value(?,?)";
PreparedStatement ps=conn.prepareStatement(sql);
ps.setString(1,f.getName());
ps.setObject(2,data);
ps.executeUpdate();

}

最后由于刚刚说过Clob类型读取字符的长度问题,这里再给大家一段代码,希望对你有帮助
public static String getClobString(ResultSet rs, int col) {
try {
Clob c=resultSet.getClob(2);
Reader reader=c.getCharacterStream():
if (reader == null) {
return null;
}
StringBuffer sb = new StringBuffer();
char[] charbuf = new char[4096];
for (int i = reader.read(charbuf); i > 0; i = reader.read(charbuf)) {
sb.append(charbuf, 0, i);
}
return sb.toString();
} catch (Exception e) {
return "";
}
}

另外似乎前面还提到过LIKE检索的问题。LONGVARCHAR类型中不可以用LIKE查找(至少ORA中不可以使用,其他的数据库我没有试过),在ORA中我们可以使用这样一个函数dbms_lob.instr来代替LIKE来个例子吧

select docid,dat0 from text where dbms_lob.instr(dat0,'魏',1,1)>0

你可能感兴趣的:(java)