定义:Java语言中,对于变量,常量,函数,语句块还有名字,我们统统称之为Java标识符。标识符是用来给类、对象、方法、变量、接口和自定义数据类型命名的。
通俗理解为:凡是自己可以起名字的地方都叫标识符,都遵守标识符的规则。
Java标识符由数字,字母和下划线(_),美元符号($)组成。
并且必须遵守以下规则:
1.Java标识符只能由数字、字母、下划线“_”或“$”符号以及Unicode字符集组成
2.Java标识符必须以字母、下划线“_”或“$”符号以及Unicode字符集开头
3.Java标识符不可以是Java关键字、保留字(const、goto)和字面量(true、false、null)
4.Java标识符区分大小写,是大小写敏感的
- 下面的标识符是合法的:
- myName,My_name,Points,$points,_sys_ta,OK,_23b,_3_
- 下面的标识符是非法的:
- 25name,#name,class,&time,if
Java标识符应遵守以下命名约定:
1.类和接口名。每个字的首字母大写,含有大小写。例如:
- MyClass,HelloWorld,Time
2.方法名。首字符小写,其余的首字母大写,含大小写。尽量少用下划线。例如:
- myMethod,myGetData//这种命名方法叫做驼峰式命名。
3.常量名。基本数据类型的常量名使用全部大写字母,字与字之间用下划线分隔。对象常量可大小混写。例如:
- SIZE_NAME,SIZE_AGE
4.变量名。可大小写混写,首字符小写,字间分隔符用字的首字母大写。不用下划线,少用美元符号。给变量命名是尽量做到见名知义。例如:
- nameLength,currentTime
关键字是电脑语言里事先定义的,有特别意义的标识符,有时又叫保留字,还有特别意义的变量。
Java的关键字对java的编译器有特殊的意义,他们用来表示一种数据类型,或者表示程序的结构等,关键字不能用作变量名、方法名、类名、包名。
上图中的关键字都不能使用,其中const与goto虽然未被使用,但也作为Java关键字保留,不能使用。
常量是一种标识符,它的值在运行期间恒定不变。并且常量在程序中只能被引用,而不能被重新赋值。
常量的命名规则:
1.在JAVA中,在变量声明中加入final关键字代表常量,加入static关键字代表类变量。一般情况下,我们把static与final结合起来声明一个常量.
2.尽量使用含义直观的常量来表示那些将在程序中多次出现的数字或字符串。
- public static final double PI = 3.1415926;
在Java接口中声明一个变量,编译时会自动加上public static final的修饰符。自动声明为全局常量,因而在Java接口通常是存放常量的最佳地点。
Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域。
Java程序中每一个变量都属于特定的数据类型,在使用前必须对其声明,声明格式为:
- type varName [=value][{,varName[=value]}];
- int a = 10;
- String b = "hello";
- int d = 3,e,f = 5;
type 是Java 的数据类型之一。该标识符是该变量的名称。申报指定类型的多个变量,用逗号分隔的列表。
从本质上讲,变量其实是内存中的一小块内存,使用变量名来访问这块区域。
因此,每一个变量在使用前必须要先申报(声明),然后必须进行赋值(填充内容),才能使用。
局部变量:方法或语句块内部定义的变量叫做局部变量。
成员变量:方法外部、类的内部定义的变量叫做成员变量。
- public void addData(){
- int a = 10;//变量"a"就是局部变量,在方法的大括号之外是无法使用"a"的。
- }
成员变量又可分为:类变量和实例变量。
- public class TestAdd{
- static final int B = 10;//变量"b"是成员变量,在整个类的内部都可以使用。
- }
类变量:又叫静态变量,用static修饰,它可以直接用类名调用,也可以用对象调用,而且所有对象的同一个类变量都是共享同一块内存空间。static final变量,必须在声明的时候初始化或者在static静态块里面初始化。
实例变量:不用static修饰, 它只能通过对象调用, 而且所有对象的同一个实例变量是共享不同的内存空间的。
注意:如果某个方法中的局部变量的名字与全局变量的名字相同, 则该全局变量在这个方法中暂时失效
变量作用域是指程序中变量的名字可以被引用的部分。这样说可能比较抽象,变量的作用域通俗上可以理解为一个变量在声明后,程序中哪部分可以访问声明的变量。
变量作用域被称变量的可访问性。在Java中有多种规则:
1.在语句块中定义的变量只能在块中被访问。这种变量的作用域是声明它的块内
2.内部块中定义的变量可以访问外部块中定义的变量
3.作为方法参数定义的变量可以在方法体内部访问
4.类变量在类中的任何地方都可以访问
5.如果内部块中定义了和外部块中相同名称的变量,则内部块中的变量会覆盖掉外部块中的变量定义
举一个例子:
- public class Test{
- int a = 10;
- public void getData(){
- int b = 5;
- for(int i = 0; i < b; i++){
- int c = 9;
- }
- }
- }
- 变量a在Test类的内部任何地方都是可以访问的,变量b只能在方法内部可以访问,变量c只能在for循环内部起作用。
数据类型可分为两种:
1:基本数据类型
- 1)整数型
- 包括:byte,short,int,long
- 2)浮点型
- 包括:float,double
- 3)字符型
- 包括:char
- 4)布尔型
- 包括:boolean
2:引用数据类型
- 1)类
- 包括:class
- 2)接口
- 包括:interface
- 3)数组
boolean类型适用于逻辑运算,一般用于程序流程控制。
boolean类型数据只允许取值true或false,不能以0或非0的整数替代true和false,并且一定要小写。
使用方法:
- boolean b1=false;//直接赋值
- boolean b2=3>4;//由条件表达式赋值
- boolean b3=b1;//由另一个boolean变量赋值
- boolean b4=list.ready();//由方法返回赋值,假设ready方法返回一个boolean的话
- 程序中boolean为布尔类型变量
实例中使用方法:
- boolean flag = false;
- if(!flag){
- //do something
- }
char类型数据用来表示通常意义上的“字符”。
字符常量为用单引号括起来的单个字符串。例如
- char eChar = 'a';
- char aChar = '中';
char 在java中是2个字节。java采用unicode,2个字节(16位)来表示一个字符。而一个中文字符的Unicode就是2个字节。
注意:Java中不推荐使用char类型存储字符数据,通常使用string或者stringbuffer存储。
整数型包含四中类型:int(整型),long(长整型),short(短整型),byte(字节型)
它们都定义了一个整数,唯一区别就是它们能够表示的数据的范围。
它们都有固定的表数范围和字段长度,其不收具体操作系统的影响,以保证Java程序的可移植性。
取值范围:
- 类型 占用存储空间 取值范围
- byte 1字节 [-2(7)~2(7)-1] -128到127之间的任意整数
- short 2字节 [-2(15)~2(15)-1] -32768~32767之间的任意整数
- int 4字节 [-2(31)~2(31)-1] -231到231-1之间的任意整数
- long 8字节 [-2(63)~2(63)-1] -263到263-1之间的任意整数
能够表示的范围越大,占用的内存空间就越大,因此在程序设计中,应当选择最合适的类型来定义整数。
与整数类型类似,Java浮点类型有固定的表数范围和字段长度,不受平台影响
存储小数的变量数据类型---浮点数,这个名称是相对于定点数而言的,这个点就是小数点。小数点可以根据需要改变位置。
在Java语言中有两种浮点数类型:float、double。
其中float是单精度型,占用32位内存空间,而double是双精度型,占用64位内存空间。
数值包含小数点或指数,或者在数字后面带有字母F or f(float), D or d(double)
float可以精确到7位有效数字,第8位的数字是第9位数字四舍五入上取得的;
double可以精确到16位有效数字,第17位的数字是第18位数字四舍五入上取得的
- double a = 3.4;
- float f = 4.3f;//不加f编译出错,损失精度
类可以看成是创建Java对象的模板。
类是客观存在的,抽象的,概念的东西。
类必须使用class关键字来表明这是一个类。
既然是类,java允许类不加public关键字约束,当然类的定义只能限制为public或者无限制关键字(默认的)。
例如一个人类:
- public class Person{
- }
- 或者
- class Person{
- }
上述实例中,public关键字是这个类的访问权限,class关键字表明这是一个类,Person是类的名称。
main()方法的声明为:public static void main(String args[])。必须这么定义,这是Java的规范。
当一个类中有main()方法,执行命令“java 类名”则会启动虚拟机执行该类中的main方法。
由于JVM在运行这个Java应用程序的时候,首先会调用main方法,调用时不实例化这个类的对象,而是通过类名直接调用因此需要是限制为public static。
对于java中的main方法,jvm有限制,不能有返回值,因此返回值类型为void。main方法中还有一个输入参数,类型为String[],这个也是java的规范,main()方法中必须有一个入参,类细必须String[],至于字符串数组的名字,这个是可以自己设定的,根据习惯,这个字符串数组的名字一般和sun java规范范例中mian参数名保持一致,取名为args。
因此,main()方法定义必须是:“public static void main(String 字符串数组参数名[])”。
HelloWord实例:
- public class HelloWord{
- public static void main(String [] args){
- System.out.print("HelloWord");
- }
- }
Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java支持4种不同的访问权限。
默认访问修饰符-不使用任何关键字
接口里的变量都隐式声明为public static final,而接口里的方法默认情况下访问权限为public。
请注意以下方法继承的规则:
1.父类中声明为public的方法在子类中也必须为public。
2.父类中声明为protected的方法在子类中要么声明为protected,要么声明为public。不能声明为private。
3.父类中默认修饰符声明的方法,能够在子类中声明为private。
4.父类中声明为private的方法,不能够被继承。
实例:
- class Test1{
- private static int age = 1;
- public static int age2 = 2;
- protected static int age3 = 3;
- static int age4 = 4;
- }
- public class Test2 extends Test1{
- public static void main(String [] args){
- //System.out.print(age);//no
- System.out.print(age2);//yes
- System.out.print(age3);//yes
- System.out.print(age4);//yes
- }
- }
算术运算符用在数学表达式中,它们的作用和在数学中的作用一样。下表列出了所有的算术运算符。
下面的实例中假设整数变量A的值为10,变量B的值为20:
- A + B等于30; //+加法- 相加运算符两侧的值
- A – B等于-10; //-减法- 左操作数减去右操作数
- A * B等于200; //*乘法- 相乘操作符两侧的值
- B / A等于2; ///除法- 左操作数除以右操作数
- B % A等于0; //%取模- 右操作数除左操作数的余数
- B + +等于21; //++自增- 操作数的值增加1
- B - -等于19; //-自减- 操作数的值减少1
具体实例:
- public class Test {
- public static void main(String args[]) {
- int a = 10;
- int b = 20;
- int d = 30;
- System.out.println("a + b = " + (a + b) );
- System.out.println("a++ = " + (a++) );
- System.out.println("d++ = " + (d++) );
- System.out.println("++d = " + (++d) );
- }
- }
- 其他于此类似
关系运算符生成的是一个“布尔”(Boolean)结果。它们评价的是运算对象值之间的关系。若关系是真实的,关系表达式会生成true(真);若关系不真实,则生成false(假)。关系运算符包括小于(<)、大于(>)、小于或等于(<=)、大于或等于(>=)、等于(==)以及不等于(!=)。
- == 检查如果两个操作数的值是否相等,如果相等则条件为真
- != 检查如果两个操作数的值是否相等,如果值不相等则条件为真
- > 检查左操作数的值是否大于右操作数的值,如果是那么条件为真
- < 检查左操作数的值是否小于右操作数的值,如果是那么条件为真
- >= 检查左操作数的值是否大于或等于右操作数的值,如果是那么条件为真
- <= 检查左操作数的值是否小于或等于右操作数的值,如果是那么条件为真
具体实例:
- int a = 10;
- int b = 20;
- System.out.println("a == b = " + (a == b) );
- System.out.println("a != b = " + (a != b) );
Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等类型。
位运算符作用在所有的位上,并且按位运算。
- & 按位与操作符,当且仅当两个操作数的某一位都非0时候结果的该位才为1。
- | 按位或操作符,只要两个操作数的某一位有一个非0时候结果的该位就为1。
- ^ 按位异或操作符,两个操作数的某一位不相同时候结果的该位就为1。
- 〜 按位补运算符翻转操作数的每一位。
- << 按位左移运算符。左操作数按位左移右操作数指定的位数。
- >> 按位右移运算符。左操作数按位右移右操作数指定的位数。
- >>> 按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。
具体实例:
- int a = 60; // 60 = 0011 1100
- int b = 13; // 13 = 0000 1101
- int c = 0;
- c = a & b; // 12 = 0000 1100
- System.out.println("a & b = " + c );
- c = a << 2; // 240 = 1111 0000
- System.out.println("a << 2 = " + c );
条件运算符也被称为三元运算符。该运算符有3个操作数,并且需要判断布尔表达式的值。该运算符的主要是决定哪个值应该赋值给变量。
语法为:
- variable x = (expression) ? value if true : value if false;
- 简写:
- x ? y : z;
上述解释为:其中x为boolean类型表达式,先计算x的值,若为true,则整个条件运算的结果为表达式y的值。否则整个运算结果为表达式z的值。
实例如下:
- int scope = 80; int x = -100;
- int flag = x > 0 ? 1 : (x == 0 ? 0 : -1);//结果为-1,先计算后面括号中的,再计算外面的
一个if语句包含一个布尔表达式和一条或多条语句。
If语句的用语法如下:
- if(布尔表达式)
- {
- //如果布尔表达式为true将执行的语句
- }
如果布尔表达式的值为true,则执行if语句中的代码块。否则执行If语句块后面的代码。
if...else语句if语句后面可以跟else语句,当if语句的布尔表达式值为false时,else语句块会被执行。
if…else的用法如下:
- if(布尔表达式){
- //如果布尔表达式的值为true
- }else{
- //如果布尔表达式的值为false
- }
if语句后面可以跟else..if…else语句,这种语句可以检测到多种可能的情况。
使用if,else if,else语句的时候,需要注意下面几点:
1.if语句至多有1个else语句,else语句在所有的elseif语句之后。2.If语句可以有若干个elseif语句,它们必须在else语句之前。3.一旦其中一个else if语句检测为true,其他的else if以及else语句都将跳过执行。语法
if...else语法格式如下:
- if(布尔表达式 1){
- //如果布尔表达式 1的值为true执行代码
- }else if(布尔表达式 2){
- //如果布尔表达式 2的值为true执行代码
- }else if(布尔表达式 3){
- //如果布尔表达式 3的值为true执行代码
- }else {
- //如果以上布尔表达式都不为true执行代码
switch语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。
switch语法格式如下:
switch语句有如下 规则:
- switch(expression){
- case value :
- //语句
- break; //可选
- case value :
- //语句
- break; //可选
- //你可以有任意数量的case语句
- default : //可选
- //语句
- }
1.switch语句中的变量类型只能为byte、short、int或者char。
2.switch语句可以拥有多个case语句。每个case后面跟一个要比较的值和冒号。
3.case语句中的值的数据类型必须与变量的数据类型相同,而且只能是常量或者字面常量。
4.当变量的值与case语句的值相等时,那么case语句之后的语句开始执行,直到break语句出现才会跳出switch语句。
5.当遇到break语句时,switch语句终止。程序跳转到switch语句后面的语句执行。case语句不必须要包含break语句。如果没有break语句出现,程序会继续执行下一条case语句,直到出现break语句。
6.switch语句可以包含一个default分支,该分支必须是switch语句的最后一个分支。default在没有case语句的值和变量值相等的时候执行。default分支不需要break语句
实例:
- char grade = 'C';
- switch(grade)
- {
- case 'A' :
- System.out.println("Excellent!");
- break;
- case 'B' :
- case 'C' :
- System.out.println("Well done");
- break;
- default :
- System.out.println("Invalid grade");
- }
while是最基本的循环,它的结构为:
- while( 布尔表达式 ) {
- //循环内容
- }
在执行时,如果布尔表达式的结果为真,则循环中的动作将被执行。这将继续下去,只要该表达式的结果为真。
在这里,while循环的关键点是循环可能不会永远运行。当表达式进行测试,结果为 false,循环体将被跳过,在while循环之后的第一个语句将被执行。
实例:
- int x = 10;
- while( x < 20 ) {
- System.out.print("value of x : " + x );
- x++;
- System.out.print("\n");
- }
对于while语句而言,如果不满足条件,则不能进入循环。但有时候我们需要即使不满足条件,也至少执行一次。
do…while循环和while循环相似,不同的是,do…while循环至少会执行一次。
- do {
- //代码语句
- }while(布尔表达式);
注意:布尔表达式在循环体的后面,所以语句块在检测布尔表达式之前已经执行了。 如果布尔表达式的值为true,则语句块一直执行,直到布尔表达式的值为false。
实例:
- int x = 10;
- do{
- System.out.print("value of x : " + x );
- x++;
- System.out.print("\n");
- }while( x < 20 );
虽然所有循环结构都可以用while或者do...while表示,但Java提供了另一种语句 : for循环,使一些循环结构变得更加简单。
for循环执行的次数是在执行前就确定的。语法格式如下:
- for(初始化; 布尔表达式; 更新) {
- //代码语句
- }
- 例:
- for(int i = 0; i < 10; i++){
- System.out.print(i+" ");
- }
关于for循环有以下几点说明:
1.最先执行初始化步骤。可以声明一种类型,但可初始化一个或多个循环控制变量,也可以是空语句。
2.然后,检测布尔表达式的值。如果为true,循环体被执行。如果为false,循环终止,开始执行循环体后面的语句。
3.执行一次循环后,更新循环控制变量。
4.再次检测布尔表达式。循环执行上面的过程。
for的另一种形式:
- 主要用于数组
- for(声明语句 : 表达式)
- {
- //代码句子
- }
- 例:
- int [] numbers = {10, 20, 30, 40, 50};
- for(int x : numbers ){
- System.out.print( x );
- System.out.print(",");
- }
声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。
表达式:表达式是要访问的数组名,或者是返回值为数组的方法。
break主要用在循环语句或者switch语句中,用来跳出整个语句块。
break跳出最里层的循环,并且继续执行该循环下面的语句。
break的用法很简单,就是循环结构中的一条语句:
- break;
实例:
- int [] numbers = {10, 20, 30, 40, 50};
- for(int x : numbers ) {
- if( x == 30 ) {
- //当x是30的时候,跳出当前循环
- break;
- }
- System.out.print( x );
- System.out.print("\n");
- }
continue适用于任何循环控制结构中。作用是让程序立刻跳转到下一次循环的迭代。
在for循环中,continue语句使程序立即跳转到更新语句。
在while或者do…while循环中,程序立即跳转到布尔表达式的判断语句。
continue就是循环体中一条简单的语句:
- continue;
实例:
- for(int i=0;i<10;i++){
- if(i==3){
- //i等于3停止当前循环,进入下一次循环
- continue;
- }
- System.out.println("i="+i);
- }
注意:break是跳出当前循环,执行循环外的下一句代码;continue是停止当前循环,进入下一次循环,并没有跳出循环
Java语言中提供的数组是用来存储固定大小的同类型元素。
首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法:
- dataType[] arrayRefVar; // 首选的方法
- 或
- dataType arrayRefVar[]; // 效果相同,但不是首选方法
Java语言使用new操作符来创建数组,语法如下:
- arrayRefVar = new dataType[arraySize];
上面的语法语句做了两件事:
一、使用dataType[arraySize]创建了一个数组。
二、把新创建的数组的引用赋值给变量 arrayRefVar。
数组变量的声明,和创建数组可以用一条语句完成,如下所示:
- dataType[] arrayRefVar = new dataType[arraySize];
另外,你还可以使用如下的方式创建数组。
- dataType[] arrayRefVar = {value0, value1, ..., valuek};
数组的元素是通过索引访问的。数组索引从0开始,所以索引值从0到arrayRefVar.length-1。
实例:
- double[] myList = new double[10];
多维数组其实就是以数组为元素的数组;
- int a[][] = {{1,2},{3,4,5,6},{7,8,9}} ; //二维数组
数组的元素类型和数组的大小都是确定的,所以当处理数组元素时候,我们通常使用基本循环或者foreach循环。
foreach语句是for语句的特殊简化版本,但是foreach语句并不能完全取代for语句,然而,任何的foreach语句都可以改写为for语句版本。
foreach并不是一个关键字,习惯上将这种特殊的for语句格式称之为“foreach”语句。从英文字面意思理解foreach也就是“for 每一个”的意思。实际上也就是这个意思。
foreach的语句格式:
- for(元素类型t 元素变量x : 遍历对象obj){
- 引用了x的java语句;
- }
实例:
- 使用foreach遍历数组元素
- double[] myList = {1.9, 2.9, 3.4, 3.5};
- // 打印所有数组元素
- for (double element: myList) {
- System.out.println(element);
- }
如果要引用数组或者集合的索引,则foreach语句无法做到,foreach只能遍历数组或者集合。
字符串广泛应用在Java编程中,在Java中字符串属于对象,Java提供了String类来创建和操作字符串。
从表面上看,字符串就是双引号之间的数据
注意:String类是不可改变的,所以你一旦创建了String对象,那它的值就无法改变了
语法为:
- String varName = "varValue";
- 或
- String varName = new String("varValue");
- 或使用构造方法,这里只是简单写一种
- char[] helloArray = { 'h', 'e', 'l', 'l', 'o', '.'};
- String helloString = new String(helloArray);
简单介绍一些string类的常用方法,具体的方法使用请参考Java API。
- int length();
- 返回字符串长度
- String[] split(String regex);
- 分割这个字符串围绕给定的正则表达式匹配.
- boolean equals(Object anObject);
- 比较此字符串与指定的对象。
- substring(int beginIndex, int endIndex);
- 返回一个新的字符串,它是此字符串的一个子字符串.
- replace(char oldChar, char newChar);
- 返回从newChar更换oldChar所有出现在此字符串中产生一个新的字符串。
- String toString()
- 返回此对象本身(它已经是一个字符串!)。
- String concat(String str)
- 将指定字符串连接到此字符串的结尾。
- String trim()
- 返回字符串的副本,忽略前导空白和尾部空白。
当对字符串进行修改的时候,需要使用StringBuffer和StringBuilder类。
和String类不同的是,StringBuffer和StringBuilder类的对象能够被多次的修改,并且不产生新的未使用对象。
StringBuilder类在Java 5中被提出,它和StringBuffer之间的最大不同在于StringBuilder的方法不是线程安全的(不能同步访问)。
由于StringBuilder相较于StringBuffer有速度优势,所以多数情况下建议使用StringBuilder类。然而在应用程序要求线程安全的情况下,则必须使用StringBuffer类。
语法:
- StringBuffer varName = new StringBuffer("varValue");
实例:
- StringBuffer sBuffer = new StringBuffer(" test");
- sBuffer.append(" String Buffer");
- System.ou.println(sBuffer);
- sBuffer的值已经被改变。结果为:test String Buffer
StringBuffer类中的方法主要偏重于对于字符串的操作,例如追加、插入和删除等,这个也是StringBuffer类和String类的主要区别。实际开发中,如果需要对一个字符串进行频繁的修改,建议使用StringBuffer。
- public StringBuffer append(String s)
- 将指定的字符串追加到此字符序列。
- public StringBuffer reverse()
- 将此字符序列用其反转形式取代。
- public delete(int start, int end)
- 移除此序列的子字符串中的字符。
- public insert(int offset, int i)
- 将 int 参数的字符串表示形式插入此序列中。
- replace(int start, int end, String str)
- 使用给定 String 中的字符替换此序列的子字符串中的字符。
- setCharAt(int index,char str) 方法
- 用来修改指定位置的字符
- deleteCharAt(int index)
- 用来删除指定位置的字符,并将剩余的字符形成新的字符串
更多方法,请参阅Java API。
简单地说,就是一个变量和常量的关系。
StringBuffer对象的内容可以修改;而String对象一旦产生后就不可以被修改,重新赋值其实是两个对象。
StringBuffer的内部实现方式和String不同,StringBuffer在进行字符串处理时,不生成新的对象,在内存使用上要优于String类。所以在实际使用时,如果经常需要对一个字符串进行修改,例如插入、删除等操作,使用StringBuffer要更加适合一些。
String:String定义的字符串具有一个很大的优点:编译器可以把字符串设为共享的。
StringBuffer:避免添加多个字符对字符串重新分配内存。
StringBuffer是线程安全的,执行效率慢。
代码风格虽然不影响程序的运行,但对程序的可读性却非常重要。自己编写的程序要让别人看懂,首先在排版方面要非常注意
1:声明变量,等号两边有空格,每次只声明一个变量,需要时才声明,并尽快进行初始化。
2:方法声明,右括号和左大括号中间有空格。
3:if语句,比较连接符(>)左右有空格,小括号和大括号中间有空格。 if 与 左括号中间有空格 。
4:注意行级注释和块级注释,方法、类、变量等需要加注释。
5:保证一行一条语句。
6:每当开始一个新的块,缩进增加2个空格,当块结束时,缩进返回先前的缩进级别。缩进级别适用于代码和注释。
7:方法名、变量名尽量做到见名知意
8:Java文件注释头在实际项目中还是要写的
代码风格没有规定的要求,主要看自己编程习惯。尽量参考Java API编程风格,不要标新立异。
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。继承可以理解为一个对象从另一个对象获取属性的过程。
继承中最常使用的两个关键字是extends和implements。
这两个关键字的使用决定了一个对象和另一个对象是否是IS-A(是一个)关系。
通过使用这两个关键字,我们能实现一个对象获取另一个对象的属性。
所有java的类均是由java.lang.Object类继承而来的,所以Object是所有类的祖先类,而除了Object外,所有类必须有一个父类。
继承两种方式:
- <code>
- extends方式
- public class Animal(){}
- public class Dog extends Animal(){}
- 通过使用关键字extends,子类可以继承父类的除private属性外所有的属性。
- implements方式
- public interface Animal(){}
- public class Dog implements Animal(){}
- Implements关键字使用在类继承接口的情况下, 这种情况不能使用关键字extends。
- </code>
可以使用 instanceof 运算符来检验dog对象是否是Animal类的一个实例。
- <code>
- interface Animal{}
- public class Dog extends Mammal{
- public static void main(String args[]){
- Dog d = new Dog();
- System.out.println(d instanceof Animal);//true
- }
- }
- </code>
Java只支持单继承(继承基本类和抽象类),但是我们可以用接口来实现(多继承接口来实现),脚本结构如:
- <code>
- public class Apple extends Fruit implements Fruit1, Fruit2{}
- </code>
一般我们继承基本类和抽象类用extends关键字,实现接口类的继承用implements关键字。
重写是子类对父类的允许访问的方法的实现过程进行重新编写!返回值和形参都不能改变。即外壳不变,核心重写!
重写的好处在于子类可以根据需要,定义特定于自己的行为。
也就是说子类能够根据需要实现父类的方法。
在面向对象原则里,重写意味着可以重写任何现有方法。
当需要在子类中调用父类的被重写方法时,要使用super关键字。
实例:
- <code>
- Dog类重写Animal的move方法
- class Animal{
- public void move(){
- System.out.println("动物可以移动");
- }
- }
- class Dog extends Animal{
- public void move(){
- System.out.println("狗可以跑和走");
- }
- }
- </code>
重写的规则
- 参数列表必须完全与被重写方法的相同;
- 返回类型必须完全与被重写方法的返回类型相同;
- 访问权限不能比父类中被重写的方法的访问权限更高。例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。
- 父类的成员方法只能被它的子类重写。
- 声明为final的方法不能被重写。
- 声明为static的方法不能被重写,但是能够被再次声明。
- 如果一个方法不能被继承,那么该方法不能被重写。
- 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为private和final的方法。
- 子类和父类不在同一个包中,那么子类只能够重写父类的声明为public和protected的非final方法。
- 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
- 构造方法不能被重写。
- 如果不能继承一个方法,则不能重写这个方法。
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型呢?可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
只能重载构造函数
实例:
- <code>
- public int test(){
- System.out.println("test1");
- }
- public void test(int a){
- System.out.println("test2");
- }
- </code>
重载规则
- 被重载的方法必须改变参数列表;
- 被重载的方法可以改变返回类型;
- 被重载的方法可以改变访问修饰符;
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载。
重写与重载之间的区别
- <code>
- 区别点 重载方法 重写方法
- 参数列表 必须修改 一定不能修改
- 返回类型 可以修改 一定不能修改
- 异常 可以修改 可以减少或删除,一定不能抛出新的或者更广的异常
- 访问 可以修改 一定不能做更严格的限制(可以降低限制)
- </code>
多态是同一个行为具有多个不同表现形式或形态的能力。
多态性是对象多种表现形式的体现。
在Java中,所有的对象都具有多态性,因为任何对象都能通过IS-A测试的类型和Object类。
如果A继承B,则A对象是B对象的一个分类,IS-A关系。
访问一个对象的唯一方法就是通过引用型变量。
引用型变量只能有一种类型,一旦被声明,引用型变量的类型就不能被改变了。
引用型变量不仅能够被重置为其他对象,前提是这些对象没有被声明为final。还可以引用和它类型相同的或者相兼容的对象。它可以声明为类类型或者接口类型。
- <code>
- public interface Vegetarian{}
- public class Animal{}
- public class Deer extends Animal implements Vegetarian{}
- public class Test {
- public static void main(String [] args){
- Deer d = new Deer();
- Animal a = d;
- Vegetarian v = d;
- Object o = d;
- }
- }
- 所有的引用型变量d,a,v,o都指向堆中相同的Deer对象。
- </code>
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
在Java语言中使用abstract class来定义抽象类。如下实例:
- <code>
- public abstract class Employee{}
- </code>
如果你想设计这样一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类确定,那么你可以在父类中声明该方法为抽象方法。
abstract关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体。
抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。
- <code>
- public abstract double computePay();
- </code>
声明抽象方法会造成以下两个结果:
- 如果一个类包含抽象方法,那么该类必须是抽象类。
- 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
继承抽象方法的子类必须重载该方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该抽象方法,否则,从最初的父类到最终的子类都不能用来实例化对象。
在面向对象程式设计方法中,封装(英语:Encapsulation)是指,一种将抽象性函式接口的实作细节部份包装、隐藏起来的方法。
封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
要访问该类的代码和数据,必须通过严格的接口控制。
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
实例
- <code>
- private String name;
- public void setName(String newName){
- name = newName;
- }
- 使用对象调用setName方法即可赋值,但无法直接访问name属性
- </code>
类属性被保护起来,不再让外界直接访问,而是提供一些列开放方法。
通常情况下,这些方法被称为getter和setter方法。
因此,任何要访问类中私有成员变量的类都要通过这些getter和setter方法。
接口(英文:Interface),在java编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在Java中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口与类相似点:
- 一个接口可以有多个方法。
- 接口文件保存在.java结尾的文件中,文件名使用接口名。
- 接口的字节码文件保存在.class结尾的文件中。
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法。
- 接口不能包含成员变量,除了static和final变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多重继承。
接口的声明语法格式如下:
- <code>
- [可见度] interface 接口名称 [extends 其他的类名] {
- // 声明变量
- // 抽象方法
- }
- </code>
interface关键字用来声明一个接口。下面是接口声明的一个简单例子。
- <code>
- import java.lang.*;
- public interface NameOfInterface
- {
- //任何类型 final, static 字段
- //抽象方法
- }
- </code>
接口有以下特性:
- 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
- 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键子。
- 接口中的方法都是公有的。
接口的实现:
- 当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。
- 类使用implements关键字实现接口。在类声明中,implements关键字放在class声明后面。
实现一个接口的语法,可以使用这个公式:
- <code>
- ... implements 接口名称[, 其他接口, 其他接口..., ...] ...
- </code>
实例:
- <code>
- public class MammalInt implements Animal{}
- </code>
重写接口中声明的方法时,需要注意以下规则:
- 类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常。
- 类在重写方法时要保持一致的方法名,并且应该保持相同或者相兼容的返回值类型。
- 如果实现接口的类是抽象类,那么就没必要实现该接口的方法。
在实现接口的时候,也要注意一些规则:
- 一个类可以同时实现多个接口。
- 一个类只能继承一个类,但是能实现多个接口。
- 一个接口能继承另一个接口,这和类之间的继承比较相似。
一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。例:
- <code>
- public interface Sports{}
- public interface Football extends Sports{}
- </code>
多重继承
在Java中,类的多重继承是不合法,但接口允许多重继承,。
在接口的多重继承中extends关键字只需要使用一次,在其后跟着继承接口。 如下所示:
- <code>
- public interface Hockey extends Sports, Event
- </code>
以上的程序片段是合法定义的子接口,与类不同的是,接口允许多重继承,而 Sports及 Event 可能定义或是继承相同的方法
标记接口最常用的继承接口是没有包含任何方法的接口。
标记接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情。
标记接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。
例如:java.awt.event包中的MouseListener接口继承的java.util.EventListener接口定义如下:
- <code>
- package java.util;
- public interface EventListener
- {}
- </code>
没有任何方法的接口被称为标记接口。标记接口主要用于以下两种目的:
1:建立一个公共的父接口:正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。
2:向一个类添加数据类型:这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型。
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。
比如说,你的代码少了一个分号,那么运行出来结果是提示是错误java.lang.Error;如果你用System.out.println(11/0),那么你是因为你用0做了除数,会抛出java.lang.ArithmeticException的异常。
异常发生的原因有很多,通常包含以下几大类:
- 用户输入了非法数据。
- 要打开的文件不存在。
- 网络通信时连接中断,或者JVM内存溢出。
这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。
要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:
- 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
- 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
所有的异常类是从java.lang.Exception类继承的子类。
Exception类是Throwable类的子类。除了Exception类外,Throwable还有一个子类Error 。
Java程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。
Error用来指示运行时环境发生的错误。
例如,JVM内存溢出。一般地,程序不会从错误中恢复。
异常类有两个主要的子类:IOException类和RuntimeException类。
在Java 内置类中,有大部分常用检查性和非检查性异常。
使用try和catch关键字可以捕获异常。try/catch代码块放在异常可能发生的地方。
try/catch代码块中的代码称为保护代码,使用 try/catch的语法如下:
- <code>
- try
- {
- // 程序代码
- }catch(ExceptionName e1)
- {
- //Catch 块
- }
- </code>
catch语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try后面的catch块就会被检查。
如果发生的异常包含在catch块中,异常会被传递到该catch块,这和传递一个参数到方法是一样。
例如:
- <code>
- try{
- int a[] = new int[2];
- System.out.println("Access element three :" + a[3]);
- }catch(ArrayIndexOutOfBoundsException e){
- System.out.println("Exception thrown :" + e);
- }
- </code>
一个try代码块后面跟随多个catch代码块的情况就叫多重捕获。
多重捕获块的语法如下所示:
- <code>
- try{
- // 程序代码
- }catch(异常类型1 异常的变量名1){
- // 程序代码
- }catch(异常类型2 异常的变量名2){
- // 程序代码
- }catch(异常类型2 异常的变量名2){
- // 程序代码
- }
- </code>
上面的代码段包含了3个catch块。
可以在try语句后面添加任意数量的catch块。
如果保护代码中发生异常,异常被抛给第一个catch块。
如果抛出异常的数据类型与ExceptionType1匹配,它在这里就会被捕获。
如果不匹配,它会被传递给第二个catch块。
如此,直到异常被捕获或者通过所有的catch块。
例如:
- <code>
- try
- {
- file = new FileInputStream(fileName);
- x = (byte) file.read();
- }catch(IOException i)
- {
- i.printStackTrace();
- return -1;
- }catch(FileNotFoundException f) //Not valid!
- {
- f.printStackTrace();
- return -1;
- }
- </code>
如果一个方法没有捕获一个检查性异常,那么该方法必须使用throws 关键字来声明。throws关键字放在方法签名的尾部。
也可以使用throw关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
下面方法的声明抛出一个RemoteException异常:
- <code>
- import java.io.*;
- public class className
- {
- public void deposit(double amount) throws RemoteException
- {
- // Method implementation
- throw new RemoteException();
- }
- //Remainder of class definition
- }
- </code>
一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。
finally关键字
finally关键字用来创建在try代码块后面执行的代码块。
无论是否发生异常,finally代码块中的代码总会被执行。
在finally代码块中,可以运行清理类型等收尾善后性质的语句。
finally代码块出现在catch代码块最后,语法如下:
- <code>
- try{
- // 程序代码
- }catch(异常类型1 异常的变量名1){
- // 程序代码
- }catch(异常类型2 异常的变量名2){
- // 程序代码
- }finally{
- // 程序代码
- }
- </code>
注意下面事项:
- catch不能独立于try存在。
- 在try/catch后面添加finally块并非强制性要求的。
- try代码后不能既没catch块也没finally块。
- try,catch, finally块之间不能添加任何代码。
在Java中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
- 所有异常都必须是Throwable的子类。
- 如果希望写一个检查性异常类,则需要继承Exception类。
- 如果你想写一个运行时异常类,那么需要继承RuntimeException 类。
可以像下面这样定义自己的异常类:
- <code>
- class MyException extends Exception{
- }
- </code>
只继承Exception 类来创建的异常类是检查性异常类。
一个异常类和其它任何类一样,包含有变量和方法。
在Java中定义了两种类型的异常和错误。
- JVM(Java虚拟机)异常:由JVM抛出的异常或错误。例如:NullPointerException类,ArrayIndexOutOfBoundsException类,ClassCastException类。
- 程序级异常:由程序或者API程序抛出的异常。例如IllegalArgumentException类,IllegalStateException类。
Enumeration接口中定义了一些方法,通过这些方法可以枚举(一次获得一个)对象集合中的元素。
这种传统接口已被迭代器取代,虽然Enumeration还未被遗弃,但在现代代码中已经被很少使用了。尽管如此,它还是使用在诸如Vector和Properties这些传统类所定义的方法中,除此之外,还用在一些API类,并且在应用程序中也广泛被使用。
下面总结了一些Enumeration声明的方法:
- <code>
- boolean hasMoreElements( )
- 测试此枚举是否包含更多的元素。
- Object nextElement( )
- 如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。
- </code>
实例:
- <code>
- Enumeration days;
- Vector dayNames = new Vector();
- dayNames.add("Sunday");
- dayNames.add("Monday");
- days = dayNames.elements();
- while (days.hasMoreElements()){
- System.out.println(days.nextElement());
- }
- </code>
一个Bitset类创建一种特殊类型的数组来保存位值。BitSet中数组大小会随需要增加。这和位向量(vector of bits)比较类似。
这是一个传统的类,但它在Java 2中被完全重新设计。
BitSet定义了两个构造方法。
- <code>
- 第一个构造方法创建一个默认的对象:
- BitSet()
- 第二个方法允许用户指定初始大小。所有位初始化为0。
- BitSet(int size)
- </code>
例如:
- <code>
- BitSet bits1 = new BitSet(16);
- // set some bits
- for(int i=0; i<16; i++) {
- if((i%2) == 0) bits1.set(i);
- }
- System.out.println("Initial pattern in bits1: ");
- System.out.println(bits1);
- </code>
Vector类实现了一个动态数组,也能通过索引访问。和ArrayList和相似,但是两者是不同的:
Vector是同步访问的。
Vector包含了许多传统的方法,这些方法不属于集合框架。
Vector主要用在事先不知道数组的大小,或者只是需要一个可以改变大小的数组的情况。
Vector类支持4种构造方法。
- <code>
- 第一种构造方法创建一个默认的向量,默认大小为10:
- Vector()
- 第二种构造方法创建指定大小的向量。
- Vector(int size)
- 第三种构造方法创建指定大小的向量,并且增量用<em>incr</em>指定. 增量表示向量每次增加的元素数目。
- Vector(int size,int incr)
- 第四中构造方法创建一个包含集合c元素的向量:
- Vector(Collection c)
- </code>
例如:
- <code>
- Vector v = new Vector(3, 2);
- System.out.println("Initial size: " + v.size());
- System.out.println("Initial capacity: " +
- v.capacity());
- </code>
栈(Stack)实现了一个后进先出(LIFO)的数据结构。
你可以把栈理解为对象的垂直分布的栈,当你添加一个新元素时,就将新元素放在其他元素的顶部。
当你从栈中取元素的时候,就从栈顶取一个元素。换句话说,最后进栈的元素最先被取出。
堆栈只定义了默认构造函数,用来创建一个空栈。
- <code>
- Stack()
- </code>
堆栈除了包括由Vector定义的所有方法,也定义了自己的一些方法。
- <code>
- boolean empty()
- 测试堆栈是否为空。
- Object peek( )
- 查看堆栈顶部的对象,但不从堆栈中移除它。
- Object pop( )
- 移除堆栈顶部的对象,并作为此函数的值返回该对象。
- Object push(Object element)
- 把项压入堆栈顶部。
- int search(Object element)
- 返回对象在堆栈中的位置,以 1 为基数。
- </code>
实例如下:
- <code>
- public class StackDemo {
- static void showpush(Stack st, int a) {
- st.push(new Integer(a));
- System.out.println("push(" + a + ")");
- System.out.println("stack: " + st);
- }
- public static void main(String args[]) {
- Stack st = new Stack();
- System.out.println("stack: " + st);
- showpush(st, 42);
- showpush(st, 66);
- showpush(st, 99);
- try {
- showpop(st);
- } catch (EmptyStackException e) {
- System.out.println("empty stack");
- }
- }
- }
- </code>
Dictionary类已经过时了。在实际开发中,你可以实现Map接口来获取键/值的存储功能。
Map接口中键和值一一映射. 可以通过键来获取值。
给定一个键和一个值,你可以将该值存储在一个Map对象. 之后,你可以通过键来访问对应的值。
当访问的值不存在的时候,方法就会抛出一个NoSuchElementException异常.
当对象的类型和Map里元素类型不兼容的时候,就会抛出一个 ClassCastException异常。
当在不允许使用Null对象的Map中使用Null对象,会抛出一个NullPointerException 异常。
当尝试修改一个只读的Map时,会抛出一个UnsupportedOperationException异常。
实例:
- <code>
- Map m1 = new HashMap();
- m1.put("Zara", "8");
- System.out.print("\t" + m1);
- </code>
Hashtable是原始的java.util的一部分, 是一个Dictionary具体的实现 。
然而,Java 2 重构的Hashtable实现了Map接口,因此,Hashtable现在集成到了集合框架中。它和HashMap类很相似,但是它支持同步。
像HashMap一样,Hashtable在哈希表中存储键/值对。当使用一个哈希表,要指定用作键的对象,以及要链接到该键的值。
然后,该键经过哈希处理,所得到的散列码被用作存储在该表中值的索引。
Hashtable定义了四个构造方法。
- <code>
- 第一个是默认构造方法:
- Hashtable()
- 第二个构造函数创建指定大小的哈希表:
- Hashtable(int size)
- 第三个构造方法创建了一个指定大小的哈希表,并且通过<em>fillRatio</em>指定填充比例。
- 填充比例必须介于0.0和1.0之间,它决定了哈希表在重新调整大小之前的充满程度:
- Hashtable(int size,float fillRatio)
- 第四个构造方法创建了一个以M中元素为初始化元素的哈希表。
- 哈希表的容量被设置为M的两倍。
- Hashtable(Map m)
- </code>
例如:
- <code>
- Hashtable balance = new Hashtable();
- Enumeration names;
- String str;
- balance.put("Zara", new Double(3434.34));
- balance.put("Mahnaz", new Double(123.22));
- names = balance.keys();
- while(names.hasMoreElements()) {
- str = (String) names.nextElement();
- System.out.println(str + ": " +
- balance.get(str));
- }
- //Zara: 3434.34
- //Mahnaz: 123.22
- </code>
Properties 继承于 Hashtable表示一个持久的属性集.属性列表中每个键及其对应值都是一个字符串。
Properties 类被许多Java类使用。例如,在获取环境变量时它就作为System.getProperties()方法的返回值。
Properties 定义如下实例变量.这个变量持有一个Properties对象相关的默认属性列表。
- <code>
- Properties defaults;
- </code>
Properties类定义了两个构造方法.
- <code>
- 第一个构造方法没有默认值。
- Properties()
- 第二个构造方法使用propDefault作为默认值。两种情况下,属性列表都为空:
- Properties(Properties propDefault)
- </code>
在书写程序的时候,我们常常需要对大量的对象引用进行管理。为了实现有效的归类管理,我们常常将同类的引用放置在同一数据容器中。
由于数据容器中存放了我们随时可能需要使用到的对象引用,所以一般的数据容器要都要能能提供方便的查询、遍历、修改等基本接口功能。
早期的OOP语言都通过数组的方式来实现对引用集的集中管理和维护。
但是数组方式下,数组大小需要提前被确定,并不允许修改大小,导致其作为一种灵活的数据容器的能力的功能大为下降。
Java中容器如下图:
List容器是有序的,可重复的。
LinkedList :其数据结构采用的是链表,此种结构的优势是删除和添加的效率很高,但随机访问元素时效率较ArrayList类低。
ArrayList:其数据结构采用的是线性表,此种结构的优势是访问和查询十分方便,但添加和删除的时候效率很低。
List是一个接口,不能实例化,需要实例化一个ArrayList或者LinkedList。
例如:
- <code>
- List al = new ArrayList();
- al.add("a");
- al.add("b");
- al.add("c");
- al.add("a");
- System.out.print(al);
- //结果为[a,b,c,a],怎么存就怎么取,有序的,可重复
- </code>
List接口的更多方法,请参考Java API。
Set(集):集合中的对象不按特定方式排列,并且没有重复对象,它的有些实现类能对集合中的对象按特定方式排列.
Set接口主要有两个实现类HashSet和TreeSet,HashSet类按照哈希算法来存取集合中的对象,存取速度比较快,HashSet类还有一个子类LinkedHashSet类,不仅实现了哈希算法,而且实现了链表数据结构,TreeSet类实现了SortedSet接口,具有排序功能.
例如:
- <code>
- Set s1 = new HashSet();
- s1.add("a");
- s1.add("b");
- s1.add("c");
- s1.add("a");
- System.out.print(s1);
- //结果为[b,c,a],结果为无序的,不可重复,所以只有一个a
- </code>
Set接口的更多方法,请参考Java API。
Map:一组成对的“键值对”对象,即其元素是成对的对象,最典型的应用就是数据字典,并且还有其它广泛的应用。另外,Map可以返回其所有键组成的Set和其所有值组成的Collection,或其键值对组成的Set,并且还可以像数组一样扩展多维Map,只要让Map中键值对的每个“值”是一个Map即可。
例如:
- <code>
- Map m = new HashMap();
- m.put("a","a1");
- m.put("b","a1");
- m.put("c","a1");
- m.put("d","a1");
- System.out.print(m);
- </code>
Map更多用法,请参考Java API。
Java泛型(generics)是JDK 5中引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
Java泛型方法和泛型类支持程序员使用一个方法指定一组相关方法,或者使用一个类指定一组相关的类型。
使用Java泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。
下面是定义泛型方法的规则:
- 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的)。
- 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
- 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
- 泛型方法方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。
例如:
- <code>
- // 泛型方法 printArray
- public static < E > void printArray( E[] inputArray )
- {
- // 输出数组元素
- for ( E element : inputArray ){
- System.out.printf( "%s ", element );
- }
- System.out.println();
- }
- //在main方法里
- Integer[] intArray = { 1, 2, 3, 4, 5 };
- System.out.println( "Array integerArray contains:" );
- printArray( intArray ); // 传递一个整型数组
- //结果为:Array integerArray contains:1 2 3 4 5 6
- </code>
更多泛型应用,请参考Java API。
Java给多线程编程提供了内置的支持。一个多线程程序包含两个或多个能并发运行的部分。
程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径。
多线程是多任务的一种特别的形式。多线程比多任务需要更小的开销。
这里定义和线程相关的另一个术语:进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。
一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守候线程都结束运行后才能结束。
多线程能满足程序员编写非常有效率的程序来达到充分利用CPU的目的,因为CPU的空闲时间能够保持在最低限度。
线程经过其生命周期的各个阶段。下图显示了一个线程完整的生命周期。
新状态: 一个新产生的线程从新状态开始了它的生命周期。它保持这个状态知道程序start这个线程。
运行状态:当一个新状态的线程被start以后,线程就变成可运行状态,一个线程在此状态下被认为是开始执行其任务
就绪状态:当一个线程等待另外一个线程执行一个任务的时候,该线程就进入就绪状态。当另一个线程给就绪状态的线程发送信号时,该线程才重新切换到运行状态。
休眠状态: 由于一个线程的时间片用完了,该线程从运行状态进入休眠状态。当时间间隔到期或者等待的时间发生了,该状态的线程切换到运行状态。
终止状态: 一个运行状态的线程完成任务或者其他终止条件发生,该线程就切换到终止状态。
创建一个线程,最简单的方法是创建一个实现Runnable接口的类。
实例:
- <code>
- public class testThread
- {
- public static void main(String [] args){
- Runner1 r1 = new Runner1();
- Thread t = new Thread(r1);
- t.start();
- for(int i = 0; i< 10; i++){
- System.out.println("main thread = "+ i);
- }
- }
- }
- class Runner1 implements Runnable
- {
- public void run(){
- for(int i = 0; i < 10; i++){
- System.out.println("Runner1 = " +i);
- }
- }
- }
- </code>
启动一个线程必须调用Thread类的start方法。
创建一个线程的第二种方法是创建一个新的类,该类继承Thread类,然后创建一个该类的实例。
继承类必须重写run()方法,该方法是新线程的入口点。它也必须调用start()方法才能执行。
实例:
- <code>
- public class testThread
- {
- public static void main(String [] args){
- Runner1 r1 = new Runner1();
- //Thread t = new Thread(r1);
- r1.start();
- for(int i = 0; i< 10; i++){
- System.out.println("main thread = "+ i);
- }
- }
- }
- class Runner1 extends Thread
- {
- public void run(){
- for(int i = 0; i < 10; i++){
- System.out.println("Runner1 = " +i);
- }
- }
- }
- </code>
创建一个新类继承Thread类,继承之后当前类就是一个Thread类,所以直接调用自身的start方法即可。
Thread类的常用方法
- <code>
- public void start()
- 使该线程开始执行;Java 虚拟机调用该线程的run方法。
- public void run()
- 如果该线程是使用独立的Runnable运行对象构造的,则调用该Runnable对象的run方法;否则,该方法不执行任何操作并返回。
- public static void yield()
- 暂停当前正在执行的线程对象,并执行其他线程。
- public static void sleep(long millisec)
- 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
- public static Thread currentThread()
- 返回对当前正在执行的线程对象的引用。
- </code>
在多线程编程时,你需要了解以下几个概念:
- 线程同步
- 线程间通信
- 线程死锁
- 线程控制:挂起、停止和恢复
多线程的使用
- 有效利用多线程的关键是理解程序是并发执行而不是串行执行的。例如:程序中有两个子系统需要并发执行,这时候就需要利用多线程编程。
- 通过对多线程的使用,可以编写出非常高效的程序。不过请注意,如果你创建太多的线程,程序执行的效率实际上是降低了,而不是提升了。
请记住,上下文的切换开销也很重要,如果你创建了太多的线程,CPU花费在上下文的切换的时间将多于执行程序的时间!
java.util包提供了Date类来封装当前的日期和时间。 Date类提供两个构造函数来实例化Date对象。
第一个构造函数使用当前日期和时间来初始化对象。
- <code>
- Date( )
- </code>
第二个构造函数接收一个参数,该参数是从1970年1月1日起的微秒数。
- <code>
- Date(long millisec)
- </code>
实例:
- <code>
- Date d = new Date();
- System.out.print(d.toString());
- //Fri May 22 09:17:12 CST 2015
- </code>
SimpleDateFormat是一个以语言环境敏感的方式来格式化和分析日期的类。SimpleDateFormat允许你选择任何用户自定义日期时间格式来运行
实例:
- <code>
- Date dNow = new Date( );
- SimpleDateFormat ft =
- new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss");
- System.out.println("Current Date: " + ft.format(dNow));
- //Current Date: 2015-05-22 09:29:48
- </code>
printf方法可以很轻松地格式化时间和日期。使用两个字母格式,它以t开头并且以下面表格中的一个字母结尾.
- <code>
- // 初始化 Date 对象
- Date date = new Date();
- // 使用toString()显示日期和时间
- String str = String.format("Current Date/Time : %tc", date );
- System.out.printf(str);
- </code>
printf:日期和时间转换字符
- <code>
- 字符 描述
- c 完整的日期和时间
- F ISO 8601 格式日期
- D U.S. 格式日期 (月/日/年)
- T 24小时时间
- r 12小时时间
- R 24小时时间,不包含秒
- Y 4位年份(包含前导0)
- y 年份后2位(包含前导0)
- C 年份前2位(包含前导0)
- B 月份全称
- b 月份简称
- n 2位月份(包含前导0)
- d 2位日子(包含前导0)
- e 2位日子(不包含前导0)
- A 星期全称
- a 星期简称
- j 3位年份(包含前导0)
- H 2位小时(包含前导0), 00 到 23
- k 2位小时(不包含前导0), 0 到 23
- I 2位小时(包含前导0), 01 到 12
- l 2位小时(不包含前导0), 1 到 12
- M 2位分钟(包含前导0)
- S 2位秒数(包含前导0)
- L 3位毫秒(包含前导0)
- N 9位纳秒(包含前导0)
- P 大写上下午标志
- p 小写上下午标志
- z 从GMT的RFC 822数字偏移
- Z 时区
- s 自 1970-01-01 00:00:00 GMT的秒数
- Q 自 1970-01-01 00:00:00 GMT的毫妙
- </code>
正则表达式定义了字符串的模式。
正则表达式可以用来搜索、编辑或处理文本。
正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。
Java正则表达式和Perl的是最为相似的。
java.util.regex包主要包括以下三个类:
- <code>
- Pattern类:
- pattern对象是一个正则表达式的编译表示。Pattern类没有公共构造方法。要创建一个Pattern对象,你必须首先调用其公共静态编译方法,它返回一个Pattern对象。该方法接受一个正则表达式作为它的第一个参数。
- Matcher类:
- Matcher对象是对输入字符串进行解释和匹配操作的引擎。与Pattern类一样,Matcher也没有公共构造方法。你需要调用Pattern对象的matcher方法来获得一个Matcher对象。
- PatternSyntaxException:
- PatternSyntaxException是一个非强制异常类,它表示一个正则表达式模式中的语法错误。
- </code>
捕获组是把多个字符当一个单独单元进行处理的方法,它通过对括号内的字符分组来创建。
例如,正则表达式(dog) 创建了单一分组,组里包含"d","o",和"g"。
捕获组是通过从左至右计算其开括号来编号。例如,在表达式((A)(B(C))),有四个这样的组:
- ((A)(B(C)))
- (A)
- (B(C))
- (C)
可以通过调用matcher对象的groupCount方法来查看表达式有多少个分组。groupCount方法返回一个int值,表示matcher对象当前有多个捕获组。
还有一个特殊的组(组0),它总是代表整个表达式。该组不包括在groupCount的返回值中。
实例:
- <code>
- // 按指定模式在字符串查找
- String line = "This order was placed for QT3000! OK?";
- String pattern = "(.*)(\\d+)(.*)";
- // 创建 Pattern 对象
- Pattern r = Pattern.compile(pattern);
- // 现在创建 matcher 对象
- Matcher m = r.matcher(line);
- if (m.find( )) {
- System.out.println("Found value: " + m.group(0) );
- System.out.println("Found value: " + m.group(1) );
- System.out.println("Found value: " + m.group(2) );
- } else {
- System.out.println("NO MATCH");
- }
- //Found value: This order was placed for QT3000! OK?
- //Found value: This order was placed for QT300
- //Found value: 0
- //上述代码表示:0下标表示整串字符串,1下标表示最后一个数字0之前的字符串,2位表示最后一个数字,3位表示最后
- //一个数字之后的字符串
- </code>
/ | 将下一字符标记为特殊字符、文本、反向引用或八进制转义符。 |
^ | 匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与"\n"或"\r"之后的位置匹配。 |
$ | 匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与"\n"或"\r"之前的位置匹配。 |
\S | 匹配任何非空白字符。与 [^ \f\n\r\t\v] 等效。 |
+ | 一次或多次匹配前面的字符或子表达式。例如,"zo+"与"zo"和"zoo"匹配,但与"z"不匹配。+ 等效于 {1,}。 |
? | 零次或一次匹配前面的字符或子表达式。例如,"do(es)?"匹配"do"或"does"中的"do"。? 等效于 {0,1}。 |
x|y | 匹配 x 或 y。例如,'z|food' 匹配"z"或"food"。'(z|f)ood' 匹配"zood"或"food"。 |
[xyz] | 字符集。匹配包含的任一字符。例如,"[abc]"匹配"plain"中的"a"。 |
[^xyz] | 反向字符集。匹配未包含的任何字符。例如,"[^abc]"匹配"plain"中"p","l","i","n"。 |
[a-z] | 字符范围。匹配指定范围内的任何字符。例如,"[a-z]"匹配"a"到"z"范围内的任何小写字母。 |
[^a-z] | 反向范围字符。匹配不在指定的范围内的任何字符。例如,"[^a-z]"匹配任何不在"a"到"z"范围内的任何字符。 |
\b | 匹配一个字边界,即字与空格间的位置。例如,"er\b"匹配"never"中的"er",但不匹配"verb"中的"er"。 |
\d | 数字字符匹配。等效于 [0-9]。 |
\D | 非数字字符匹配。等效于 [^0-9]。 |
\n | 换行符匹配。等效于 \x0a 和 \cJ。 |
\r | 匹配一个回车符。等效于 \x0d 和 \cM。 |
\w | 匹配任何字类字符,包括下划线。与"[A-Za-z0-9_]"等效。 |
更多匹配规则,请查阅Java 正则表达式相关文档。
1 | public int start() 返回以前匹配的初始索引。 |
2 | public int start(int group) 返回在以前的匹配操作期间,由给定组所捕获的子序列的初始索引 |
3 | public int end()返回最后匹配字符之后的偏移量 |
4 | public int end(int group)返回在以前的匹配操作期间,由给定组所捕获子序列的最后字符之后的偏移量。 |
- private static final String REGEX = "\bcat\b";
- private static final String INPUT ="cat cat cat cattie cat";
- public static void main( String args[] ){
- Pattern p = Pattern.compile(REGEX);
- Matcher m = p.matcher(INPUT); // 获取 matcher 对象
- int count = 0;
- while(m.find()) {
- count++;
- System.out.println("Match number "+count);
- System.out.println("start(): "+m.start());
- System.out.println("end(): "+m.end());
- }
- }
实例
- <code>
- private static String REGEX = "dog";
- private static String INPUT = "The dog says meow. " +
- "All dogs say meow.";
- private static String REPLACE = "cat";
- public static void main(String[] args) {
- Pattern p = Pattern.compile(REGEX);
- // get a matcher object
- Matcher m = p.matcher(INPUT);
- INPUT = m.replaceAll(REPLACE);
- System.out.println(INPUT);
- }
- //The cat says meow. All cats say meow.
- </code>
Matcher 类也提供了appendReplacement 和appendTail 方法用于文本替换:
看下面的例子来解释这个功能:
- <code>
- private static String REGEX = "a*b";
- private static String INPUT = "aabfooaabfooabfoob";
- private static String REPLACE = "-";
- public static void main(String[] args) {
- Pattern p = Pattern.compile(REGEX);
- // 获取 matcher 对象
- Matcher m = p.matcher(INPUT);
- StringBuffer sb = new StringBuffer();
- while(m.find()){
- m.appendReplacement(sb,REPLACE);
- }
- m.appendTail(sb);
- System.out.println(sb.toString());
- }
- //-foo-foo-foo-
- </code>