郑志远的java学习笔记
郑志远 CCB
1 Java起源
略
2 Java语言概述
1.面向对象编程的3个原则:封装(encapsulation)、继承(inheritance)、多态性(polymorephism)
封装:将代码及其处理的数据捆绑在一起的一种机制;
继承:一个对象获得另一个对象的属性的过程;
多态性:一个接口,多种方法。
2.System.out.println(“dfjdfjdf”);
System.out.print(“dfdfdfdf”);
这两种写法输出基本一样,只是print不换行
3.基本词汇:空白分隔符(whitespace)标识符(identifiers)常量(literal)注释(comments)分隔符(separators)java关键字 java类库
3 数据类型、变量、数组
1.Java语言是强类型语言,java定义了8个简单数据类型:byte short int long char float double Boolean
2.如果一个数据类型占8位那么他的范围为-(2的7次方)到2的7次方-1,一共2的8次方个数字(别忘记0),其中最高为作为符号位,是1就为负值。
其中:(2的8次方-1)等于2的0次方+2的1次方+……+2的7次方
所以IP地址中若地址为00000000则代表可以有256个地址,0次方+2的1次方+……+2的7次方=255,但是别忘记0 !!!
3.标准的ASCII字符集的范围为0~127(共2的7次方个,因为最高位没用),扩展的ASCII字符集范围为0~255(共2的8次方个)
UNICODE字符集占16位,范围为:0~65535(共2的16次方个)
4. System.out.println(“The value is”+ (10>9)); 结果为true,10>9加括号,因为+的优先级大于”>”
System.out.println(“10>9”); 结果为10>9
5.Octal------8进制 ,前面加0表示
Hexadecimal------16进制,前面加0x 或0X表示
6.Java中的浮点字面量默认是双精度的,占64位
1234.333f(orF)---------float
1234.343434d(or D)-------double
所以:float m=20.3将编译出错,应该写为float m=(float)20.3
7.自动类型转换:
满足以下两个条件:这2种类型是兼容的; 目的类型的范围比源类型的范围大
强制类型转换:
2种不兼容类型之间的转换,格式为:(target-type) value,例如:将整形转为字节型,若整形的值超出byte的取值范围,则转化后的值为对256取摸。Byte b; int i=300; b=(byte)i; 则b的值为44。
例如:将323.234强制转为byte的步骤:去掉小数点后的数为323,在执行323%256得到67。
8.自动类型提升,用在表达式中,byte和short自动提升为int型
例如:
byte b=50;
b=b*2;//编译将出错!因为b*2为整形
应该写为
b=(byte)(b*2)//括号的优先级大于*,所以b*2要加括号
又例如:
short mm=(short)0x8000;//此处的(short)也必须写,否则编译出错!!!
9.数组,声明格式:
int array[]=new int[3];
或者
int array[];
array=new int[3];
//初始化
array[0]=1;
array[1]=2;
array[2]=3;
或者
int array[]={1,2,3};//此时自动分配内存,而不用new操作符
注意:[]之内不可写进长度,否则编译出错!!!
另外Java进行数组越界检查,而C/C++没有
多维数组:
int Dshuzu[][]=new int[2][3];
初始化
Dshuzu[0][0]=1;……………
或者 int Dshuzu[][]={
{12,13,14},
{20,21,22}
};//[]之内不可写进长度,否则编译出错!!!
可以先个第一维分配内存,第二维手工分配内存。例如:
int Dshuzu[][]=new int[3][];
Dshuzu[0]=new int[1];
Dshuzu[1]=new int[2];
Dshuzu[2]=new int[3];
第二维的大小不相等,图例:
Dshuzu[0][0]
Dshuzu[1][0] Dshuzu[1][1]
Dshuzu[2][0] Dshuzu[2][1] Dshuzu[2][2]
另外的数组声明方法:
int[] mm = new int[12];
int[][] nn = new int[3][4];
在java中数组是作为对象来使用的。数组有一个实例变量length,存储的是数组能够包含的元素的个数。
例如:
char arr[]=new char [10];//不论字符数组,还是整形数组,元素个数最多为10个,下标从0-9
arr.length;//他的值为10
int arr1[20]={1,3,4,5};
arr1.length//他的值为20
char str[][]=new char[2][3]//此二维数组的length为2。即第一维 的长度
10.Java中方法内部声明的变量必须赋初值,虽然它有自己的默认值。例如:
int i;
system.out.println(i);//编译将出错,提示i没有初始化,解决:int i=0;
但是实例变量不存在这种情况。
4 运算符
四大运算符:算术运算符、位运算、关系运算、逻辑运算
另外还有比较运算符(instanceof)
4.1算术运算符号
算术运算符不能用在boolean上,但是可以用在char上,因为在Java中char 类型是int 类型的一个子集。
对整数进行“/”运算,余数将被舍去。例如:int i=6/4;结果为1。
%运算符也可以用在浮点数(C/C++中不允许)例如:
42.25%10的结果为2.25
注意递增递减运算 ++m和m++的区别
++m 的值和m保持同步,而m++的值比m小1。例如int m=0;
++m;//则m的值为1,++m表达式的值也为1
m++;//则m的值为1,m++表达式的值为0
有一个例子:
int i=100;int j=200;
while(++i< --j);//空循环体,i大于或等于j则循环结束
system.out.println(“:i is “ + i);//求出i和j的中间值150
算术赋值运算 例如:a+=4;等价于a=a+4;
4.2位运算符(bitwise operators)
直接对整数类型的位进行操作,包括:byte short int long char 。
理解 反码、补码。负数的2进制表示是:将原码做反码后加1。
整型都是有符号的,(除了char),所以用他们的最高位来表示符号位。
注意:对byte和short 移位运算时(不论左移还是右移),byte和short 都先被自动提升到int型。再执行移位运算。如果byte和short为负数的时候,也就是说符号位也要扩展,高位全部用1填充,随后再做移位!
例如:
byte b=01000000;
int i;
i=b<<2;
//结果为256,2进制为00000000 00000000 00000001 00000000
b=(byte)(b<<2);//结果为0,byte只有8位,高位被截断
例如: byte b=11111111;//十进制为-1
b=(byte)(b<<2);
//变为11111111 11111111 11111111 11111100,10进制为-4
b=(byte)(b>>2);
//变为11111111 11111111 11111111 11111111,10进制为-1
b=(byte)(b>>>2);
//变为00111111 11111111 11111111 11111111,b还是-1,因为b先被扩展为int型
注意:左移(<<)时,如果将1移进高位(31或63)那么值就变为负的。
高位移出,低位补0
右移运算(>>):低位移出,高位填充原来高位的值。
无符号右移(>>>):低位移出,高位用0填充。
注意:因为short 和byte 总被扩展到32位,所以符号位扩展和移动总是发生在32和64位。
掌握位赋值运算符,例如:a=a>>4;等价于 a>>=4;
a^=b; 等价于a=a^b;
4.3关系运算符
== > < <= >= !=
关系运算的结果为boolean,只有true和false,不像C/C++中,非0为true,0为false。
boolean flag= a<c;//这种写法是正确的
4.4布尔逻辑运算符
布尔逻辑运算的运算数只能是布尔型,结果也为boolean型
注意:
标准的逻辑与(&)和逻辑或(|)不论第一个表达式为true or false,都计算第二个表达式。例如:
if (c==0 | e++ < 100) d=12;//不论c是否等于0,e都被自增量。
短路AND(&&)和短路OR(||):
A&&B,当A为false时,不去计算B的值而直接返回false;当A为true时,计算B的值。
A||B,当A为true时,不去计算B的值而直接返回true; 当A为false时,计算B的值。
4.5赋值运算符(=)
int x,y,z;
x=y=z=100;//允许对一连串的变量赋值
4.6 ?运算符
三元运算符(ternary),经常用于取代if- else语句。格式:
expression1?expression2:expression3
//expression1是一个布尔表达式, expression2和expression3是除了void类型以外的任何类型的表达式,并且它们的类型必须相同。
例如:
rado=denom==0?0:num/denom;
//如果denom等于0则?表达式的值为0,否则为num/denom。最后将?表达式的值赋给rado
4.7运算符的优先级
掌握运算符的优先级。适当的运用()可以提高程序的可读性,澄清含义,而不至于降低程序的运行速度。
5 程序控制语句
Java程序控制语句分为:选择(selection)、重复(iteration)、跳转(jump)
5.1 Java的选择语句
Java支持2种选择语句:If语句和Switch语句。
5.1.1 if 条件分支语句
If (condition)//condition只能是布尔型
Statement;
Else if (condition)
Statement;
Else if (condition)
Statement;
……………
Else
Statement;
5.1.2switch多路分支语句
switch(expression) {
case value1:
statement;
break;
case value2:
statement;
break;
…………….
default:
statement;
}
//其中expression必须为byte,short,int 或者char 类型,每个case之后的value必须是与表达式类型兼容的一个常量
break用于跳出整个switch语句。如果省略break,则每个语句都被遍历,符合条件就被执行,直到switch末尾或者遇到break为止。
例如:
switch(expression) {
case value1:
case value2:
case value3:
statement;
break;
case value4:
case value5:
statement;
break;
…………….
default:
statement;
}
注意:switch 语句只能测试相等的情况,而if可计算任何类型的布尔表达式。
同一个switch语句中没有两个相同的case常量,嵌套switch的外部case常量和内部case常量除外。
Switch 语句通常比if语句更有效。
5.2循环语句
包括:while do while for
5.2.1 while循环
格式: while(condition) {
//body of loop
} //condition为布尔表达式
空循环在Java中是合法的。
空语句是只有一个分号(;)的语句。例如;
int i=100;int j=200;
while(++i < --j) //++i的值和i同步
; //;可以和while语句连一块为while(++i< --j);
system.out.println(“i=” + i);
当然上面的例子可以写为:
int i=100;int j=200;
while (i<j){
i++;j--;
}
System.out.println("i=" + i);
5.2.2 do-while循环
格式: do {
//body of loop
} while (condition);//不要漏掉;
循环至少被执行一次。
例子:
int n=10;
do {
system.out.println(“n=” + n);
n--;
} while (n>0);
可以写为以下,更为高效。
int n=10;
do {
system.out.println(“n=” + n);
} while (--n>0);//--n的值与n的值同步
5.2.3 for 循环
格式: for (initialization ;condition ; iteration) {
//body of loop
}
//如果只有一条语句需要重复则{ }没有必要。初始化表达式只被执行一次。例如for(i=0;i<10;i++) 中i=0只执行1次!!!
在for循环中声明循环控制变量。例如:
for (int i=0;i<10;i++) {
………….
}//i的作用域在for循环执行后就结束了
在for循环中使用“,”。例如:
for (a=1 , b=4;a<b ; a++ , b--)
注意:在Java中,逗号仅仅是一个分隔符,只适用于for循环!!!
for( ; ; ){
……..
}//这将是一个无限循环
掌握循环嵌套
5.3跳转语句
java支持3种跳转语句:break continue return
5.3.1 break语句
作用:1、在switch中,终止一个语句序列
2、退出一个循环
3、作为一种先进的“goto”语句使用
注意:switch语句中的break仅仅影响该switch,而不影响任何循环;
内部循环的break仅仅终止该循环,外部循环不受影响。
把break当作goto的一种形式来用(不局限于循环),最大用处是跳出多层循环。 格式:break label; 例子:
public static void main(Strings[] args)
{
label1:{
label2:{
for(i=0;i<10;i++)
{
if (i==5) break label1;//执行跳出label1,在label1的结尾开始执行
}
}
system.out.println(“not arrival here”);//不被执行
}
system.out.println(“Hello!arrivel here”);//被执行
}
程序最后输出:Hello!arrivel here
又例如:
outer:for (int m=0;m<3;m++)//循环的范围已经固定,所以用于循环的label可以不带{ }。当然也可以用{}包括更多的语句,关键是注意label的范围。
{
system.out.println(“pass”+ m);
for(int i=0;i<50;i++){
if(i==10) break outer;//若只写break;则跳出该循环,从该循环结束的位置开始执行
system.out.println(“i=”+i);
}
system.out.println(“not execute me”);
}
sytem.out.println(“I am executed”);
注意:如果label1不包括break,则break label1非法,不被编译。例如:
label1{
……………
}
for(i=0;i<10;i++){
if(i==5) break label1;//错误!!!因为label1不包括break
}
5.3.2 掌握continue的用法。
5.3.3 return语句。将在以后章节详细研究。
下面的例子将编译出错,提示:unreachable statement因为编译器知道println语句将永远不能被执行。C语言中也存在这种情况,但是编译时只提出warning.
class test{
public static void main(String[] args){
int m=100;
return;
system.out.println(“m is “+ m);
}
}
解决方法是:在return前面加一个true值以蒙骗编译器 如:
boolean flag=true;
if (flag) return;
6 介绍类
类是Java的核心和本质。
6.1类基础
类是一个逻辑构造,创建一个类就相当于创建了一种新的数据类型。
对象有物理的真实性,也就是说对象占有内存空间
对象是类的一个实例(instance),所以经常instance和object互换使用。
类包括成员变量和成员方法。成员变量包括实例变量和类(static)变量。
6.2声明对象
参见《Java2参考大全》
动态分配内存就是在运行时分配内存,通过new运算符来实现。
类的声明只是创建一个模板(或类型描述),并不创建一个实际的对象。
6.3给对象引用变量赋值
Object b1=new Object();
Object b2=b1;//b2和b1的地址值相等,指向了同一对象。
此时b1和b2引用了同一对象,并不是对象的拷贝。通过变量b2对对象的改变将影响b1所对应的对象。
b1=null;//这里b1被设置为空,但b2仍指向原来的对象。
相当于C中的指针。
6.4方法
访问类的成员有两种:
1.类内部代码的访问
2.类外部代码的访问
当一个实例变量不是被该实例变量所在类的部分代码访问时,它必须通过该对象加点运算符来访问。但是当一个实例变量被定义该变量的类的部分代码访问时,该变量可以被直接引用。同样的规则适用于方法。
写方法时必须要有返回值,不返回写void。(构造函数例外。)
否则编译时提示: invalid method declaration; return type required
例如:
class Box{
double width;
double height;
double depth;
double volume(){
return width*height*depth//没有用对象加点号
}
void setdim(double w ,double h, double d){
width=w;
height=h;
depth=d;
}
}
//一个好的Java程序,它的实例变量应该仅仅由类内部的方法来存取,不应该直接对其存取。象object obj=new object();obj.var1=10;这样就不好。如:vb。
Class demo{
Public static void main(String[] args){
Box mybox=new box();
double vol;
mybox.setdim(12,13,14);
vol=mybox.volume();
system.out.println(“volume is” + vol);
}
}
注意:下面区分一下自变量(parameter)和参数(argument)
自变量是方法定义的一个变量,当方法调用时,他接收一个值。例如上面setdim(double w ,double h, double d)方法中w,h,d就是自变量
参数是当一个方法被调用时,传递给该方法的值。例如setdim(12,13,14)把12,13,14作为参数传递,自变量w,h,d接收它们。
6.5构造函数
构造函数与类同名,在对象创建后,new运算符完成前,构造函数立即自动调用。构造函数无任何返回值,即使void型也不返回。他的任务就是初始化一个对象的内部状态,使实例变量能够完全初始化,可以被对象马上调用。
如果不显式的为类定义一个构造函数,java将为该类创建一个默认的构造函数,它将实例变量初始化为零。
6.4中的例子中将void setdim改为Box(double w ,double h, double d)即可。注意:Box必须和类同名,区分大小写!!!
6.6this关键字
在方法内代表引用该方法的对象。使用this是冗余的,但是完全正确。他在6.6.1中比较有用。
例如:
Box(double w ,double h, double d){
this.width=w;
this.height=h;
this.depth=d;
}
6.6.1隐藏的实例变量
在同一个方法内,定义二个重名的局部变量在java中是不合法的。但是,
方法内的变量和方法的自变量可以和类的实例变量的名字相同,此时,实例变量名字就被隐藏,就只好用this关键字来存取同名的实例变量。
例如:
Box(double width ,double height, double depth){
this.width=width;
this.height=height;
this.depth=depth;
}//width height depth为实例变量
6.7垃圾回收(garbage collection)
Java自动处理重新分配内存。,垃圾回收只在程序中偶尔发生,对于大多数程序,不必考虑垃圾回收问题。
6.8finalize()方法
有时撤销一个对象时,需要显式的完成一些操作,例如一个对象处理的是非java资源,如文件句柄或windows字符字体,这时,在对象被撤销以前要保证这些资源被释放。
finalize()方法正好在垃圾回收之前被调用。但是,当一个对象超出了他的作用域,finalize()并不被调用。我们不知道何时,甚至是否finalize()被调用,所以,最好用别的方法释放有对象使用的系统资源,不要依靠finalize方法。
6.9一个堆栈类
7 进一步研究方法和类
7.1方法重载(method overloaded)
方法重载是实现多态性的一种方式,由参数的类型或数量来决定重载方法的版本,虽然每个重载方法可以有不同的返回类型,但是,返回类型不足以区分所使用的是哪个方法。
7.1.1构造函数重载
可以重载多个构造方法。像上面的Box,就可以另外构造一个
Box(){
Width=1;
Height=1;
Depth=1;
}//这样比较方便,可以在只想创建一个对象而不关心它的实例变量时,不用老是输入参数
7.2把对象作为参数
参见《java参考大全114页》
对象参数最普遍的使用涉及到构造函数,构造一个新对象,使它的初始状态与一个已存在的对象一样。
例如:
Box(Box obj){
width=obj.width;
height=obj.height;
depth=obj.depth;
}
//关键:以类的名字作为数据类型,就像java的内置类型一样。传给obj 的参数自然在先前是创建好了的。和c的指针参数差不多,例如
函数:kill_blank_all(char *str),你不用在函数体内给str分配内存,因为在调用这个函数之前,作为参数的字符串内存已经分配了。
举例说明:
Box box1=new box(12,13,14);//box1被声明且分配内存并初始化了。
Box clone_box=new box(box1);//box1是一个对象
7.3参数是如何传递的
1.按值传递(call-by-value)//将参数值拷贝一份
2.引用调用(call-by-reference)//指向同一对象
注意:当一个简单类型传递给一个方法时,使用按值传递。对象传递则按引用传递。
实际上一个对象引用被传递个方法时,引用本身使用按值传递,但是因为被传递的值指向一个对象,该值的拷贝仍然指向同一个对象。
7.4返回对象
方法能返回任何类型的数据,包括你创建的类的类型。例如;
Box test_meth(){
Box box1 = new Box(12,13,14);
return box1;
}//其中蓝色的Box是返回类型,和C的返回指针一样,是个引用。
同C/C++字符指针的类比:
Box box1 =new box();
char *str0=”compare to C”;
Box box3;//先声明一个Box型的引用,但是引用哪块内存未分配,同char *str;
Box3=box1.test_meth();//将引用指向一个已经存在的对象。同str=str0;
举个数据库操作的例子:
Resultset res=conn.execute(“select * from table”);//数据库对象的方法execute的返回类型就是对象:Resultset。所以,有些地方不必显式的非要用new 来创建引用的对象。
7.5递归(recursion)
递归就是允许方法调用自身,它的典型的例子就是数字的阶乘,1---N之间的所有数的乘积。参考 大全 118页。
递归,迭代各有各的优点。
7.6介绍访问控制(access control)
如果程序的其他部分可以绕开一个类的方法(接口)而直接去存取该类的变量,说明封装的不好。可以通过访问指示符(access specifier)来控制。极端的做法是将成员变量全部设为private,只能通过类的方法对他们存取。
VB这一点作的就不好,很多属性都可以直接存取。
主要有public private protected 默认访问级别(包内public)
类中的private成员为此类私有,不能为该类外的所有代码访问,包括子类。
main()方法老是被public指示符修饰,因为他要被java运行时系统调用。若没有public,编译提示:Main method not public
7.7;理解static
如果一个成员被声明为static,他就能够在他的类的对象被创建之前被访问,不必引用任何对象。最常见的例子是main()方法,因为在程序开始时必须调用main(),所以他被声明为static。若没有static,编译提示:
Exception in thread "main" java.lang.NoSuchMethodError: main
参见大全125页
类变量:所有的实例共用一个变量。
类方法:
1.仅能直接调用其他的static方法
2.只能直接调用static变量(数据),调用任何实例变量都是错误的。 但是,可以通过声明所在类的对象来访问实例变量。
3.不能以任何方式引用this和super。因为他们代表对象。
4.类方法可以有自己的自变量。
以上几点适用于main函数,转到8。1
调用方法:
class_name.variable
class_name.method()
static块的声明方法:
static {
system.out.println(“This is a static block”);
}
调用一个类方法的顺序是:首先它所在的类的static变量被初始化一次
再者,static块被只初始化一次,后者,类方法执行。
例如:
class userstatic{
static int i=3;
static int j;
static void meth(int x){
system.out.println(“x=”+x);
system.out.println(“i=”+i);
system.out.println(“j=”+j);
}
static{
system.out.println(“static block initialized”);
b=a*4;
}
public void main(String[] args){
meth(42);
}
}
输出结果为:
static block initialized
x=42
i=3;
j=12
7.8介绍final
变量声明为final即为常量,类似于C/C++中的const。例如:
final int FLAG=1;
final变量一般都大写,在实例中不占内存,实质上是常数。虽然这样,但是仍须将类实例化后才能访问,不像static那样。
final的其他两个作用在继承中:
1.final 方法可以防止被子类覆盖,但是可以在其他类中被访问。
2.final类可以防止被继承。
7.9略
7.10介绍嵌套类和内部类
在另一个类中定义的类就是嵌套类(nested classes)。
嵌套类的两种类型:
1.前面加static标识符的嵌套类。他不能直接引用包围类的成员,只有通过对象来访问包围类的成员。所以,这种嵌套类很少使用。
2. 内部类(inner class):前面不加static标识符的嵌套类。
类B被定义在类A之内,那么B为A所知,然而不被A的外面知道。类A外面的任何代码试图实例化类B,都汇编译错误,但是类A内部的代码可以实例化B。
嵌套类可以直接引用嵌套它的类的成员变量或方法,包括private,但是,包围类不能直接引用但是可以通过对象来访问嵌套类的成员变量和方法。
注:在这里用 “可不可以访问 “不太恰当
内部类有和外部类的成员同级的,也有定义在外部类的方法之内或方法的循环之内的。
若属于后者,这内部类不但可以直接引用外部类的成员变量,还可以直接引用方法的局部变量。
例如:
class outer{
int i=10;
class inner{
void in_meth(){
system.out.println(“I is “ + i);//直接调用包围类的变量
}
}
void out_meth(){
inner in1 = new inner();//包围类实例化内部类
in1.in_meth();
}
}
体会:其实内部类、包围类的成员引用只是一个范围问题,一个以{}为界的范围。出了这个作用域,成员的生命期就结束了。
也可以从访问的两种方式来理解。类内代码访问(直接引用)和类外代码访问(对象加点访问):内部类对包围类来说是类内代码,包围类的代码对于内部类来说是类外代码。
下面看看一段C语言程序
main ()
{
int i=1000;
int j=2000;
{ //注意作用域
int i=23; //将暂时覆盖1000
int m=50;
printf("%d/n",i); //结果为23
printf("%d/n",j); //结果为2000,说明可以直接引用作用域外` 面的变量
printf("%d/n",m); //结果为50
} //作用域到此结束
printf("%d/n",i); //结果为1000,23已经释放了。
printf("%d/n",m); //编译期错误。m已经灭亡了。说明不可直接引用作用域里面的变量。
}
内部类一般不使用,但在处理applet时有帮助。还有匿名内部类(anonymous inner classes),一个没有名字的内部类。详见第20章。
7.11String类
我们创建的每一个字符串都是String类型的一个对象。
String类型的对象是不可改变的,如果要改变,可以先创建一个新的字符串,其中包含修改后的字符串即可。
StringBuffer类型的字符串可以改变。
连接字符串运算符 :+,注意:它不能用于其它任何的类型
String类的方法:
boolean equals(String object)//检验两个字符串引用指向的内容是否相等
int length()//获得字符串的长度,java字符串无’/0’这一说。
char charAt(int index)//获得字符串指定索引的字符。从0开始。
String 数组
String str[]={“abc”, “cde”,”mnodf”};
7.12命令行参数
public static void main(String[] args);
命令行参数存在args数组中,args.length为参数的个数。0为无参数,不像C中把命令自身作为第一个参数,无论如何argc都至少为1。C的命令行参数是通过char型指针数组实现的
Java和C/C++一样:命令行参数都是以字符串的形式传递。你必须手工把字符串转变为程序内部需要的类型。
比如:第一个参数为8,则args[0]=”8”,其实是字符串对象,程序内部如要用到整形,必须转换。又例如:
C:/ java command m ,则args[0]=”m”,m是字符串对象,而不是简单类型----字符。方法为:char c;c=args[0].charAt(0) 。
换作C语言,C:/command m,其实是args[1]=”m/0”,如果想把它赋给char c;则只有c=args[1][0];才正确。不要忘记C语言中args[0]等于命令自身。
8 继承(inheritance)
创建分等级层次的类。
词汇:Superclass超类 subclass子类
格式:
class subclass-name extends superclass-name {
//body of class
}//一个子类只能有一个超类(超类是抽象类也不例外),区别于C++可以继承多个基础类。
//没有类可以成为他自己的超类。
8.1继承的基础
子类包括超类的所有成员,可以直接引用这些成员,因为都继承为自己的了。但是不能访问private成员,然而可以覆盖or 重载private变量和方法。超类的static方法不可以被子类覆盖,但可重载(除非子类的该方法也用static修饰)。
体会 Static 例如:
class superc{
int m=1000;
void superfun(){
system.out.println(“This is a super class”);
}
}
class subc extends superc{
public static void main(String[] args){
system.out.println(“my m is”+m);
fun();
}
}
//上面两句好像是正确的,因为子类完全拥有父类的成员,但是,重要的一点main()方法被static修饰,只能调用类变量和类方法。因为m不是static变量,所以编译出错,提示:
non-static variable m cannot be referenced from a static context
解决方法:将public static void main()单独放在一个类中,通过subc类的对象调用成员。所以,在写java程序的时候,main()方法所在的类里面一般都不定义别的方法。例如:
class subc extends superc{
int n=19;
int subfun(){
superfun();//直接引用
return m+n;
}
}
class demo{
public static void main(String[] args){
subc subc1=new subc();
system.out.println(subc1.m);
subc1.superfun();
subc1.subfun();
}
}
超类的引用变量可以被任何从该超类派生的子类的引用赋值,反之,编译错误提示:incompatible types。但是,只能访问超类定义了的成员部分。如果出现方法覆盖:引用变量的类型决定了调用谁的成员变量;被引用对象的类型决定了调用谁的成员方法。也就是说,同名方法调用子类的,同名变量调用超类的。这叫超类引用变量引用子类对象。
例如:
class super
{
int i=100;
void fun(){
system.out.println(“super”)
}
}
class sub extends super{
int m=1;
int i=2;//覆盖了超类的i
void fun(){ //覆盖了超类的方法fun。
system.out.println(“sub”)
}
}
super s1;
sub s2=new sub();
s1=s2;or s1=new sub();
system.out.println(s1.i)//输出100
system.out.println(s1.fun())//输出sub
system.out.pritnln(s1.m)//编译出错,提示can not be applied to …..因为超类中没有它的定义
8.2使用super
1.使用super调用超类的构造函数。
格式:super(parameter-list)
注意:super函数必须是子类构造函数的第一个语句,如果不显式的写出,java会默认调用super()。而且在多层次继承中super()总是引用子类最接近的超类的构造函数。构造函数的调用顺序是按照派生顺序调用的。
例如:B是A的子类。那么在调用B的构造函数的时候,如果不显式的指明,首先默认调用的是A的构造函数A(),这时如果A中有带参数的构造函数而没有定义A(),则编译出错。如果A中没有显式的定义构造函数反而正确了。说明如果定义了A(parameter),则没有A()这一说,除非自己定义!
1.用于超类成员名被子类中同样的成员名隐藏的情况,也就是成员覆盖
格式: super.member
只有返回类型和方法名称、参数精确匹配时才称之为方法覆盖,否则变成方法重载了
体会:好的编程习惯,从超类中选择合适的构造函数,在子类的构造函数中显式地调用super(argement),除非超类没有显式的定义构造函数。
8.3创建多级类层次
B继承A,C继承B
8.6继承和运行时多态性
通过将子类的对象赋给超类的引用变量来实现。例如:
超类super,他有一个方法callme(),在控制台上输出I am super。
它有两个子类sub1和sub2,分别覆盖了super的callme()方法,输出I am sub1 和I am sub2。
super s0=new super();
sub1 s1=new sub1();
super ss;
ss=s0;
ss.callme();//结果为I am super
ss=s1;
ss.callme()//结果为I am sub1
ss=new sub2();
ss.callme()//结果为I am sub2
函数名字、参数、返回值都一样,而且是一系列相关的对象,然而返回的结果不同,这种运行时的多态性也只有通过继承中方法覆盖的超类引用变量访问子类对象来获得,还有第9章的接口。
方法覆盖是方法重载的一个特例。
8.7使用抽象类
只是定义了一个供子类共享的通用格式。
抽象方法: abstract type method-name(parameter-list);没有函数体,不要漏掉分号
抽象类 abstract class class-name{…………}
任何含有一个或多个抽象方法的类必须声明为抽象类。抽象类不能通过new操作符实例化,但是可以创建对象引用(超类类型的引用变量),指向子类,以实行运行时多态性。
注意:构造函数和静态方法都不能定义为abstract。抽象类的子类必须覆盖(而不是重载)实现超类中的抽象方法,否则子类必须被声明为abstract。
8.8使用final
final修饰符的三个作用,前面已经阐述了:1。声明常量;2。在继承中,防止方法被覆盖(除非子类的该方法也用final修饰)(但final方法可被重载);3。在继承中,防止类被继承,类被声明为final则类的所有方法(不包括成员变量)都是final的。
final方法可以提高程序性能,编译器可以内嵌调用它。称为早期绑定(early binding)。参见7。8
8.9Object类
Object类是所有其他类的超类,所以一个Object类型的引用变量可以引用任何一个类的对象,包括数组。
Object类的几个方法:
Object clone() //创建一个对象的副本
Boolean equals(Object object)//判断对象的内容是否相等
Calss getClass() //获取一个对象的类(就是从那个类实例化来的)
String toString() //返回描述对象的一个字符串,不过该方法在对象用println()方法输出时自动调用,例如:System.out.println(object);实际上就是System.out.println(object.toString())。很多类重载了该方法。
9 包和接口
Java两个革新性的特点:包和接口。
包(package):是类的容器,包以分层方式保存,是用来保存划分的类名的空间。包既是命名机制又是可见度控制机制。
接口(interface):接口自己不定义任何实现。类可以实现多个接口,而类只能继承一个超类(抽象类或其他)。
9.1包
Java用文件系统目录来存储包,目录名必须和包名严格匹配。例如:
package com.sun.zzy;
class test{
//body of class
}
则目录为:com/sun/zzy(windows); com/sun/zzy(unix)
属于该包的所有类必须存储在此母录下。
执行类:java com.sun.zzy.test 必须是包名加上类名
类路径(CLASSPATH):默认为 . 前工作目录。假设以上目录结构在C:/myjava下,则CLASSPATH应为:.;C:/myjava;………….,这样类com.sun.zzy.test可以随处执行,否则只能在C:/myjava下面执行,因为它是按照目录结构来搜索的。
9.2访问保护
类的访问级别:只有2个
默认的:仅可被同包的其他代码访问
public: 可以被任何代码访问
类成员的访问级别:
| Private成员 | 默认成员 | Protected成员 | Public成员 |
同一类中可见 | 是 | 是 | 是 | 是 |
同包中对子类可见 | 否 | 是 | 是 | 是 |
同包中对非子类可见 | 否 | 是 | 是 | 是 |
不同包中对子类可见 | 否 | 否 | 是 | 是 |
不同包中对非子类可见 | 否 | 否 | 否 | 是 |
9.3引入包
格式:
import java.util.Date;
import java.io.*;
*为引入整个包,这种形式会增加编译时间,但是对类的大小、运行时间、性能绝对没有影响。
编译器为所有程序隐式地引入基本语言功能包java.lang:import java.lang.*;
注意:当一个包被引入,仅仅是包中声明为public类可以在引入代码中对非子类可用。
9.4接口(interface)
规定一个类必须做什么,而不管你如何做。实现“一个接口,多个方法”
格式:
access-modifier interface interface-name{
return-type method1-name(parameter-list);
return-type method1-name(parameter-list);
type final-var1name;
…………
}//接口中的所有方法都没有方法体,以;结束。
access-modifier:只有public 和 默认。
接口中的变量被类实现后是final型的,就是说是常量。如果接口被public修饰,则她的所有变量和方法都是public的。
例如:
interface ShareConst{
int NO=0;
int YES=1;
int LATER=8;
}
class quer implements ShareConst{
int fun(){
return NO;
}
}
//接口中的变量的这种形式就像C中用头文件创建的#define常量
注意:一个类可以实现多个接口,并且实现接口的方法必须声明为public,返回必须类型严格匹配。
如果是非abstract类,则必须实现接口中的所有方法;否则,必须声明为abstract类,他以后的子类必须实现接口的方法,否则也必须为abstract.
实现接口:
access-modifier class class-name [extends superclass] [implements `` interface [,interface…………]] {
//body of class
}//access-modifier只能是public或 默认。
接口类型变量引用实现接口的类的对象,实行运行时多态性。但是,接口引用变量仅仅知道接口声明的方法。就像第8章的超类引用变量引用子类对象。第168页
接口的扩展:
通过继承来实现。例如:
interface A{
void meth1();
void meth2();
}
interface B extends A{
void meth3();
}
class Myclass implements B{
public void meth1(){
system.out.println(“This is meth1”);
}
public void meth2(){
system.out.println(“This is meht2”);
}
public void meth3(){
system.out.println(“This is meth3”);
}
}
注意:当一个类实现一个继承了另一个接口的接口时,它必须实现接口继承链表中的所有方法。否则,该类必须被声明为抽象类。
10 异常处理
10.1异常基础
异常是运行时错误,包括运行时用户程序错误和运行时系统本身错误。
异常类型:
Throwable是所有异常类型的超类;两个分支:
Exception :用于用户程序异常,可用来创建用户自己的异常子类。RuntimeException是一个比较重要的子类(包括被0除和非法数组索引和空指针),属于非检测异常。
其他的为检测异常。
Error :运行时系统本身错误,例如:堆栈溢出。
5个关键字:try catch throw throws finally。
异常处理:1.Java运行时系统默认提供的异常处理程序
2.用户自己处理异常,使用try 和 catch。作用:修正错误是程序继续执行,防止程序自动终止。
例如:
class excep{
public static void main(String [] args){
int m=2000;
int n=0;
int k;
try {
k=m/n;//抛出异常,程序转到catch处,try块中其他语句不再被执行
system.out.println(“k=”+k);//不被执行了
system.out.println(“I am not execute”);//不被执行
}
//system.out.println(“Hear not add any statement”)
注意:try块和catch块之间不能有任何语句,否则编译提示:’try’ without ‘catch’ or ‘finally’; ‘catch’ without ‘try’,try必须和catch或finally配合使用。
catch(Exception ee) {
system.out.println(ee.toString());//输出一个异常的描述字符串
system.out.println(ee);//同上,默认调用toString()方法
k=0;
}
system.out.pritnln(“k is set “+k); //执行
system.out.println(“This is executed”);//执行
}
}
10.2多重catch
每一个catch子句被依次检查,第一个匹配异常类型的子句被执行,其他的子句被旁路。当匹配的catch执行完后,从匹配try/catch块以后的代码开始执行。
注意:使用多catch时,异常子类必须在他的任何父类之前。如果子类在父类后面,子类将永远不会到达。Java中不能到达的代码将编译出错!
而且,同一个try的各个catch块之间不能有任何其他的语句。否则编译出错:提示'catch' without 'try',同样的,finally和try之间,finally和catch之间都不能有任何语句。
10.3嵌套try
一个try语句可以在另一个try块内部。每个try块必须有catch块或finally块与他对应,catch的异常类型与运行时抛出的类型匹配与否没有关系。如果不匹配,则检查他的外层try的catch处理是否匹配,这样直到一个与一个catch语句匹配成功。如果没有catch语句匹配,则由运行时系统默认的异常处理程序处理。
匹配的catch执行以后,程序即从此catch之后开始执行,抛出异常的语句到匹配的catch之间的部分不将被执行。例如:
class p{
public static void main(String[] args){
int m=100;
int n=0;
int i[]={1};
try {
try {
System.out.println(i[3]);//抛出异常的语句
}
catch(ArithmeticException oo){//与抛出的异常类型不符
System.out.println("Error by 0");
}
System.out.println("try1");//不被执行
System.out.println(m/n);
}
catch (ArithmeticException oo){
System.out.println("Error by 0");
}
//System.out.println("try2");
catch (ArrayIndexOutOfBoundsException ee){
System.out.println("Error by Array");
}
System.out.println("try3");
}
}//程序输出:Error by Array
try3
10.4引发(throw)
以上都是java运行时系统引发的未检测异常。可以用throw明确地引发未检测或检测异常,程序在碰到throw时立即停止 ,去寻找与其类型匹配的catch,一切流程同上。
格式: throw ThrowableInstance//抛出异常的一个实例。
例如:这是一个明确引发未检测异常的例子
class p{
public static void main(String[] args){
fun(); //由于该异常是可以捕捉的,不用添加try/catch块
}
static void fun(){//由于该异常可以捕捉的,也就是未检测异常,属于RuntimeException的子类,所以此处不用写throws ArithmeticException
System.out.println("zhengzy");
throw new ArithmeticException();//用户引发一个ArithmeticException异常
//system.out.println(“I am not executed”);//throw后面不可再有别的(包括return),因为他们肯定不可到达,编译将出错!!!
}
}
运行结果:
zhengzy
Exception in thread "main" java.lang.ArithmeticException
at p.fun(p.java:9)
at p.main(p.java:4)
10.5 throws
前提:如果一个方法throw一个检测异常但是不处理他,为了保护方法的调用者不发生异常,就用到了throws。
class p{//这是一个明确引发检测异常但是处理了的例子
public static void main(String[] args){
fun();
}
static void fun(){
System.out.println("zhengzy");
try{
throw new IllegalAccessException();
}
catch(IllegalAccessException oo){
System.out.println("zhengliping...");
}
}
} //方法fun()中throw的异常被catch处理了,所以throws就没必要了。
原则:除了Error和RuntimeException及其子类等这些非检测异常,其他的检测异常必须在方法的throws子句中声明,而且其他代码调用该方法时必须放在try/catch中以捕获检测异常。
class p{//这是一个方法明确引发检测异常但是未处理的例子
public static void main(String[] args){
try {
fun();
}
catch(IllegalAccessException oo){
System.out.println(oo); oo.printStackTrace();
}
}
static void fun()throws IllegalAccessException{
System.out.println("zhengzy");
throw new IllegalAccessException(“This is message”);
}
}结果为:zhengzy
java.lang.IllegalAccessException: This is message
其中This is message可以通过oo.getMessage()得到。
重新抛出异常:
class p{
public static void main(String[] args)throws IllegalAccessException{
try {
fun();
}
catch(IllegalAccessException oo){
throw oo;//重新抛出异常。由运行时系统的默认异常处理程序处理。
}
}
static void fun()throws {
System.out.println("zhengzy");
try {
throw new IllegalAccessException();
}
catch(IllegalAccessException oo){
throw oo;//重新抛出异常,由方法的调用者的try/catch处理
}
}
} 结果为:
zhengzy
Exception in thread "main" java.lang.IllegalAccessException
at p.fun(p.java:13)
at p.main(p.java:5)
10.6finally
finally块与try联合使用,finally块将在try结束之前执行。也就是说什么也不能阻挡finally执行,包括return,控制流语句等等。
了解用户自定义异常
class myException extends Exception{//继承Exception
private int msgnum;
myException(int a){
msgnum=a;
}
public String toString(){//一般都重载toString()方法
return “myException:”+msgnum;
}
class demo{
public fun() throws myException{
if(a>10)
throw new myException(109);
}
另外,如果try块中的代码或者调用的方法没有明确的抛出检测异常,则catch 块中就不能出现检测异常类型,否则,编译提示:p.java:14: exception java.lang.InterruptedException is never thrown in body of corresponding try statement。然而,catch块中出现非检测异常就无所谓。总而言之,如果抛出了检测异常,你就必须检测到也就是在某个catch中匹配到,而且,catch中的类型必须和抛出类型匹配或是Exception。否则编译出错,提示:p.java:12: unreported exception java.lang.InterruptedException; must be caught or declared to be thrown
11 多线程编程
线程的状态:running suspend resume block terminate
线程优先级
同步性,管程(monitor)
消息传递
Thread类和Runnable(可追捕的,可猎取的)接口,要创建一个新线程,必须扩展Thread类或实现Runnable接口。
Thread类管理线程的方法:
getName 获得线程的名称
getPriority 获得线程的优先级
isAlive 判断线程是否仍在运行
join 等待一个线程终止
run 线程的入口点
sleep 在一段时间内挂起线程
start 通过调用运行方法来启动线程