java学习笔记总结

########本文整理自疯狂java讲义及互联网#########

Java环境搭建:

  1. 下载、安装JDK:http://www.oracle.com/technetwork/java/javase/downloads/index-jsp-138363.html
  2. 配置环境变量:JAVA_HOME、PATH
  3. 打开CMD,运行javac、java检验是否安装成功

注意:习惯把运行的class名字作为文件名来保存 
如果文件中类是public修饰,那那么类名必须和文件名相同

一个java文件中可以放几个public的类?

答案:一个

Alt text

Alt text

Alt text

JAVA变量

变量是用来标识一块内存
变量必须有类型--》决定了内存区域的大小能放什么数据
注意:
1. 变量必须声明并且赋值之后才能使用,声明的时候必须有类型,赋值的时候必须类型匹配
2. 变量可以重复赋值,但不能在同一作用域内重复定义
3. 变量有作用范围,就是离它最近的大括号,不能超过大括号
4. 在实际开发中,变量的命名建议:
a. 首字母小写,有多个单词组成,驼峰式命名(每个单词首字母大写)
b. 望文生义

标识符规则
1. 标识符可以由字母、数字、下划线(_)和美元符($)组成,且数字不能开头
2.标识符不能使用关键字和保留字,但可以包含关键字和保留字
3.不能包含空格
4.标识符只能包含美元各个领域,不能包含其他特殊字符。
java中所有的关键字都是小写的

JAVA数据类型

整数

byte    1字节         8
short 2字节 很少使用
int 4字节 最常用
long 8字节 长整型

小数

float    4字节
double 8字节

其它

char    2字节    无符号,采用unicode编码,意味着支持中文
boolean 1字节 代表真 or

System.out.println(Integer.MAX_VALUE);

int g= Integer.MAX_VALUE + 1;

System.out.println(g + “,” + Integer.MIN_VALUE);

java提供了3个特殊的直接量:true、false、null

java还提供了3个特殊的浮点数值:正无穷大、负无穷大和非数,正无穷大通过Double或Float的POSITIVE_INFINITYGEFI,负无穷大通过Double或Float的NEGATIVE_INFINITY表示,非数通过Double或Float的NaN表示。

把任何基本类型的值和字符串进行连接运算时,基本类型的值将自动转换为字符串类型,虽然字符串类型不再是基本类型,而是引用类型。因此,如果希望基本类型的值转换为应对的字符串,可以把基本类型的值和一个空字符串进行连接。 
示例代码: 
public class TestString{ 
public static void main(String[] args){ 
String str1 = 3.5f +”“; 
System.out.println(str1); 
str1 = “Hello!”; 
System.out.println(3+4+str1); //这个表达式先执行3+4得到7,然后会把7当成字符串进行处理,从而得到7Hello! 
System.out.println(“hello!” +3 +4); //从左往右依次进行字符串拼接运算,最后结果是hello34 

}

//最大值加1变成最小值 
//long整型,超过int范围要加l/L 
long la = 100000000000000000L; 
long lb = 10000000000000000L; 
浮点数不能做精确运算 
负数 = 正数取反 + 1 
正数 = 负数取反 + 1 
两个整数相除,结果仍然取整数

注意:在java中,不区分变量的声明和定义

在java中定义常量:
final double PAI = 3.14
如果希望某个常量可以在一个类的多个方法中使用,通常将这些常量称为类常量。可以使用关键字static final设置一个类常量,如下:
public static final double PAI = 3.14
如果一个常量被声明为public,那么其他类的方法也可以使用这个常量。

自动类型转换:当把一个表数范围小的数值或变量赋给另一个范围大的变量时,系统将可以进行自动类型转换,自动类型转换不会损失精度
强制类型转换:把一个表数范围大的数值赋值给表数范围小的变量时,需要进行强制类型转换,语法格式是(targetType)value,把一个浮点数强制类型转换为整数时,java将直接截断浮点数的的小数部分
把一个int型整数强制类型转换为8位的byte类型时,则需要截断前面的24位,只保留右边8位,都会造成信息丢失。

注意,如下两种情况都可能报错,因为java默认会把整数和浮点数认作为intdouble
long a = 99999999999;
float b = 5.6;
正确方法如下:
long a = 99999999999l;
float b = 5.6f;
通常情况下,字符串不能直接转换为基本类型,但通过基本类型对应的包装类则可以实现把字符串转换成基本类型,例如,把字符串转换成int类型:
String a = "45";
int ia = Integer.parseInt(a);
Java为8种基本类型都提供了对应的包装类:
boolean Boolean
byte Byte
short Short
int Integer
long Long
char Character
float Float
double Double
8个包装类都提供了一个parseXxx(String str)的静态方法用于将字符串转换成基本类型

当一个算术表达式中包含多个基本类型的值时,整个算术表达式的数据类型将发生自动提升,提升规则:
所有的byte型、short型和char型将被提升到int
整个算术表达式的数据类型自动提升到与表达式中最高等级操作数同样的类型,必须注意,表达式的类型将严格保持和表达式中最高等级操作数相同的类型,下面的代码中两个int整数进行除法类型运算,即使无法除尽,也得到一个int结果

null类型的直接量为null,且可以直接赋给任何类型的变量,包括String类型

算术运算符、逻辑运算符与C/C++相同

+     -     *     /     %     ++     --

不使用中间变量交换变量值 : 
(1)加减法。

 a = a + b;

b = a - b;

a = a - b;

逻辑运算符

&& || (短路) !

条件运算符

表达式 1 ? 表达式2:表达式3
如果表达式1true结果就是表达2的结果
如果表达式1false结果就是表达3的结果

关系运算符

< > <= >= == !=

赋值运算符

=
扩展后的赋值运算符:+= -= /= *= %= &= |= ^=(异或等于) <<= >>= >>>=
只要能使用这种扩展后的赋值运算符,通常都推荐使用这种赋值运算符。因为这种运算符有更好的性能,而且程序会更加健壮

字符串连接:+

位运算操作符:

右移位:>> 有符号 正数补0 负数补1
无符号右移 >>> 正数补0 负数补0
左移位:<< 后面补0(不管正负数)
** 没有<<<运算符**
右移动一位相当于除2,正数右移一位,前面补一个0,后面抹去一位,负数前面补位1,后面抹去一位
**推理过程:**
b = -10;
b >> 2;
a.先得到-10的二进制
10的二进制: 280 1010
取反 : 281 0101 --> +1
-10的二进制 281 0110
b.移动
右移动两位 11 281 01 这就是最终的负数,但负数不好计算具体值,再采取取反加1得到正数的值
c.计算最后结果
00 280 10 + 1 = 00 280 11,结果为3,负数就为-3

位运算符

&    |     ^
按位异或^:相同为0,不同为1
不使用中间变量交换两个变量值:
int c = 10,d = 20;
c = c ^ d;
d = c ^ d;
c = c ^ d;

文档注释

通过JDK提供的javadoc工具可以直接将源代里的文档注释提取成一份系统的API文档
注:由于文档注释是用于生成API文档的,而API文档主要用于说明类、方法、属性的功能。因此javadoc工具只处理文档源文件在类、接口、方法、属性、构造器和内部类之前的注释,忽略其他地方的文档注释。
而且javadoc工具默认只处理以public 或protected修饰的类、接口、方法、属性、构造器之前的文档注释。
如果确实希望javadoc工具可以提取private修饰的内容,则可以在使用javadoc工具时增加-private选项
文档注释以斜线后紧跟两个星号(/**)开始,以星号后紧跟一具斜线(*/)作为结尾
javadoc基本用法:
javadoc 选项 java源文件/包

javadoc命令可以对源文件、包来生成API文档,在上面的语法格式中,java源文件可以支持通配符,例如使用*.java来代表当前路径下所有Java源文件。javadoc的常用选项有如下几个:
-d <目录名>:该选项指定一个路径,用于将生成的API文档放到指定目录下
-windowtitle <文本>:该选项指定个字符串,用于设置API文档的浏览器窗口标题
-doctitle <文本>:该选项指定一个HTML格式的文本,用于指定概述页面的标题。
-header <字符串>:该选项指定一个HTML格式的文本,包含每个页面的页眉
例如,为目录下每个源文件生成API文档:
javadoc -d ../apidoc -windowtitle 测试javadoc工具 -doctitle 学习javadoc工具的测试API文档 -header 自定义类 *.java
效果如下:

如果我们希望生成javadoc工具生成更详细的文档信息,例如为方法、参数返回值等,则利用javadoc标记,常用的如下:
@author:指定程序的作者
@version:指定源文件的版本
@depricated:不推荐使用的方法
@param:方法的参数说明信息

程序的结构:

顺序结构

分支结构

1.if(条件){
//语句 如果条件成立要执行的语句只有一句,大括号可以省略

2.if(条件){
...语句
}else{
...语句
}
TIPSSystem.out.print()与System.out.println()的区别是后者会换行
3.if(条件){

else if(条件){
}else{
}

4.switch(表达式){ 
case 1: 
…..; 
break; 
case 2: 
…..; 
break;

case 3:
.....;
break;

case 4:
.....;
break;
default:
.....;

} 
switch 语句语句中控制表达式的类型只能是byte、short、char、int,不能是字符串,在case标签后每个代码块需要有break才能确保只执行一个分支

循环结构

while(表达式){
........;
//迭代语句
}
do{
.....;
//迭代语句
}

while(表达式); //while不同,do while 循环的循环条件必须有一个分号,这个分号表明循环结束
for(初始化;条件判断;改变循环条件){
....;
//迭代语句
}

使用break结束循环

使用break可以强行终止循环,break不仅可以线束其所在的循环,还可直接线束其外层循环。此时需要在break后紧跟一个标签,这个标签用于标识一个外层循环
java中的标准就是一个跟着英文冒号(:)的标识符,与其他语言不同的是,java中的标签只有放在循环语句之前才有作用
使用continue结束本次循环
continuebreak的区别是中上本次循环,接着开始下一次循环。即略过当次循环中剩下的语句,重新开始新循环。
使用return语句结束方法

数组

java支持两种语法格式来定义数组:

type[] arrayName;
type arrayName[];
推荐使用第一种来定义数组
数组的初始化有两种方式:
静态初始化:初始化时由程序员显式指定每个数组元素的初始值,由系统决定需要的数组长度
动态初始化:初始化时程序员指定数组长度,由系统为数组元素分配初始值
静态初始化的语法如下:
type[] arrayName;
arryName = new type[]{elemnet1,element2..};
例如:
int[] intArr; //定义
intArr =new int[]{3,1,56,11,20}; //初始化

//将定义和初始化同时完成
int[] a = {5,6,7,11,99,2};

动态初始化的语法如下:
type[] arrayName;
arrName = new type[length];
例如:
int[] ia = new int[5];
执行动态初始化时,程序员只需指定数组的长度,即为每个数组元素指定所需的内存空间,系统负责初始化,不同类型遵循如下规则:

类型 初始值 
boolean false 
byte 0 
short 0 
int 0 
long 0 
char ‘\u0000’ 
float 0.0 
double 0.0 
类、接口、数组 null 
注意:不要静态初始化和动态化同时使用,也就是说不要在进行数组初始化时,既指定数组的长度,也为每个数组元素分配初始值

使用数组: 
所有数组都提供了一个length属性,通过这个属性可以访问到数组的长度 
例如: 
int[] a = {5,6,7,11,99,2}; 
for(int i = 0 ;i < a.length; i++){ 
System.out.println(a[i]); 
}

Alt text

Alt text

                        ![Alt text](./1449933880806.png)

Alt text

Alt text

JDK1.5提供了foreach循环

这种循环遍历数组和集合更加简洁。使用foreach循环遍历数组和集合元素时,无须获得数组和集合长度,无须根据索引来访问数组元素和集合元素, 
foreach循环自动遍历数组和集合的每个元素。

foreach循环的语法格式如下: 
for(type variableName: array |collection) 

//variableName自动迭代访问每个元素 

上面方法格式中,type是数组元素或元素的类型,variableName是一个形参名,foreach自动数组元素,集合元素赋值给该变量,如下: 
int[] a = {33,11,24,65,66} 
for(int x :a){ 
System.out.println(x); 

如果希望改变数组元素的值,则不能使用这种foreach循环

java语言里定义类的简单语法如下:

[修饰符] class 类名{

零个到多个构造器定义。。。

零个到多个属性。。。

零个到多个方法。。。

修饰符可以是public、final或者省略

定义属性的语法格式如下:

[修饰符] 属性类型 属性名 [=默认值]

修饰符可以省略,也可是public、protected、private、static、final、其中public、protected、private三个最多只能出现其中之一,可以与static、final组合起来修饰属性。

定义方法的语法格式如下:

[修饰符] 方法返回值类型 方法名(形参列表){

//由零条到多条可执行语句组成的方法体

修饰符:修饰符可以省略,也可以是public、proctected、private、static、final、abstract,其中public、protected、private三个最多只能出现其中之一,abstract和final最多只能出现其中之一,它们可以和static组合起来修饰方法。

static是一个特殊的关键字,它可用于修饰方法、属性等成员。static修饰的成员表明它是属于这个类共有的,而不是属于该类的单个实例,因为通常把static修饰的属性和方法也称为类属性、类方法

不使用static修饰的普通方法、属性则属于该类的单个实例,而不是属于该类。因为通常把不使用static修饰的属性和方法也称为实例属性、实例方法。

static修饰的属性和方法也称为静态属性、静态方法,静态成员不能直接访问非静态成员。

Alt text

static关键字不能修饰构造器

定义构造器的语法格式如下: 
[修饰符] 方法名(形参列表){ 
//由零条到多条可执行语句组成的方法体 
} 
修饰符:可以省略,也可以是public、protected、private其中之一

注意:构造器不能定义返回值类型声明,也不能使用void定义构造器没有返回值。如果为构造器定义了返回值类型,或使用了void来定义构造器没有

返回值,编译时不会出错,但java会把这个所谓的构造器当成方法来处理。

对象的产生和使用

创建对象的根本途径是构造器,通过new关键字来调用某个类的构造器即可创建这个类的实例,如下:

Person p = new Person();

如果访问权限允许,类里定义的方法和属性都可以通过类或实例来调用。类或实例访问方法或属性的方法是:类.属性|方法,或者实例.属性|方法

static修饰的方法和属性,既可以通过类来调用,也可以通过实例调用;没有使用static修饰的普通方法和属性,则只可通过实例来调用。

对象的this引用

Java提供了一个this关键字,this关键字是一个对象的默认引用。this关键字总是指向调用该方法的对象。与python中的self关键一样的作用。

java中,允许对象中的一个成员直接调用另一个成员,可以省略this前缀。

static修饰方法中不能使用this引用。由于static修饰的方法不能使用this引用,所以static修饰的方法的不能访问不使用static修饰的普通成

如果确实需要在静态方法中访问一个普通方法,则只能重新创建一个对象,如通过对象:

new Person().info()

super关键字

如果要在子类中调用父类被覆盖的实例方法,则可使用super关键字来调用父类被覆盖的实例方法 
super.fun(); 
也可以通过super()调用父类的构造函数,当需要

Java对象及其引用

方法重载

java允许一个类里定义多个同名方法,只要形参列表不同,至于方法的其他部分,如返回值类型、修饰符等,与方法重载没有任何关系 

不推荐重载具有可变形参的方法,因为这样做没有太大的意义,而且引起程序的可读性降低

方法重写


子类中与父类同名方法称为重写,也称为方法覆盖

方法的重写要遵循“两同两小一大”规则:

“两同”即同名、同参,“两小”即子类返回值、子类抛出的异常应比父类方法抛出的异常更小或相等,一大即子类方法的访问权限应比父类方法更大或相等** 

注意:重写的方法与父类必须是同类型的方法,要么都是实例方法,要么都是类方法,否则会编译错误

成员变量和局部变量

成员变量:

实例属性(不以static修饰),实例属性则可理解为实例成员变量,这作为为实例的一个成员,与实例共存亡
类属性(以static修饰),作为类的一个成员,与类共存亡
存在如下访问方法:
类.类属性
实例.实例属性
实例.类属性 此时实例访问的不是这个实例的属性,依然是访问它对应类的类属性。如果通过一个实例修改了类属性,其他实例访问这个类属性时也将获得修改过的值
成员变量不需要显示初始化,系统会在这个类的准备阶段或创建这个类的实例时进行默认初始化。

*局部变量:

形参(方法签名中定义的变量)
方法局部变量(在方法内定义)
代码块局部变量(在代码块内定义)

提示:一个类在使用之前要经过类加载、类验证、类准备、类解析、类初始化等几个阶段 
与成员变量不同的是,局部变量除了形参之外,都必必须显式初始化。

也就是说,必须先给方法局部变量和代码块局部变量指定初始值,否则不可以访问它们。

java允许局部变量和成员变量同名,如果方法里的局部变量和成员变量同名,局部变量会覆盖成员变量,如果需要在这个方法里引用被覆盖的成员变 
量,则可使用this(对于实例属性)或类名(对于类属性)作为调用者来限定访问成员变量。

变量的使用规则

与类相关的信息应该定义成类属性,与实例相关的信息则定义成实例属性。如果某个信息需要在某个类的多个方法之间进行共享,则这个信息应该使用成员变量来保存。

局部变量的使用应该遵循尽可能缩小作用范围的原则,局部变量作用范围越小,在内存中停留的时间越短,程序运行性能越好,因此能用代码块局部变量的地方就不要使用方法局部变量

隐藏和封装

理解封装

对一个类或对象实现良好的封装,可以实现以下目标:

  • 隐藏类的实现细节
  • 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里加入控制逻辑,限制对属性的不合理访问
  • 可进行数据检查,有得保证对象信息的完整性
  • 便于修改,提高代码的可维护性
  • 将对象的属性和实现细节隐藏起来,不允许外部直接访问
  • 把方法暴露出来,让方法来操作或访问这些属性

使用访问控制符

java提供了三个访问控制符:private、protected、publicc,还有一个不加任何访问控制的访问控制级别,提供了四个访问控制级别 
访问控制级别由小到大如下: 

private –> default –> protected –> public

    同一类中    同一包中    不同包中    同一包子类中  不同包子类中
public Yes Yes Yes Yes Yes
protected Yes Yes No Yes Yes
默认 Yes Yes No Yes No
private Yes No No No No

1. private:只能在该类的内部被访问。
2. default:类里的一个成员或者一个顶级类不使用任何访问控制符修饰,default访问控制的成员或顶级类可以被相同包下其他类访问。
3. protected:可以被同一个包中其他类访问,也可以被不同包中的子类访问。通常情况下protected修饰的方法,通常是希望其子类来重写 这个方法
4. public:如果一个成员或者一个顶级类使用了public来修饰,这个成员或顶级类就可以被所有类访问,不管访问类和被访问类是否处于同一包中,是否具有父子继承关系。**

顶级类可以使用public和默认访问控制符修饰,使用public修饰的顶级类可以被所有类使用,如声明变量,不使用任何访问控制符修饰的顶级类只能被同一个包中的所有类访问

注意:如果一个java源文件里定义的所有类都没有使用public修饰,则这个java源文件的文件名可以是一切合法的文件名,但如果一个java源文件里定义一个public修饰的类,则这个源文件的文件名必须与public的类名相同

关于访问控制符的使用,存在如下几条基本原则:

  • 类里的绝大部分属性都应该使用private修饰,除了一些static修饰的、类似全局变量的属性,才可能考虑使用public修饰。除此之外,有些方法只是用于辅助实现该类的其他方法,这些方法称为工具方法,工具方法也应该使用private修饰
  • 如果某个类主要用做其他类的父类,该类里的包含的大部分方法可能仅希望被其子类重写,而不想被外界直接调用,则应该使用protected修饰这些方法
  • 希望被其他类自由调用的方法应该使用public修饰。

package 和import

java的包机制提供了类的多层命名空间,用于解决类的命名冲突、类文件管理等问题
java的包机制需要2个方面保证:
1. 源文件里使用package语句指定包名
2. class文件必须放在对应的路径下

import关键字可以向某个java文件中导入指定层次下某个或者全部类,import关键字应该出现在package语言之后、类定义之前。 
例:

import java.util.Date;
immport java.util.*;

java默认为所有源文件导入java.lang包下的所有类,因此前面在java程序中使用的String、Sysem类时都无须使用import语句来导入这些类。

JDK1.5以后增加了一种静态导入的语法,它用于导入指定类的某个静态属性值或全部静态属性值

静态导入语句使用import static语句,静态导入也有两种语法,分别用于导入指定类的单个静态属性和全部静态属性

例如:

import static java.lang.System.*;
import static java.lang.Math.*;

java的常用包

java的核心类都放在java这个包及其子包下,Java扩展的许多类都放在javax包以及其子包下,这些实用类也就是前面据说的API(应用程序接口),Sun按这些类的功能分别放在不同的包下,以下是java中常用包:

java.lang:包含java语言的核心类,如String、Math、System、Thread类等,使用这个包下的类无须使用import导入,系统会自动导入这个 
包下所有的类

java.util:包含了java大量工具类、接口和集合框架类/接口,例如Arrays和List、Set等。
java.net:包含了java网络编程相关的类/接口
java.io:包含了java输入/输出相关的接口
java.text:包含了一些java格式化相关的类/接口
java.sql:包含了java进行JDBC数据库编程的相关类/接口
java.awt:包含了抽象窗口工具集(abstract window Toolkits)的相关类/接口,这些类主要用于构建图形用户界面(GUI)程序
java.swing:包含了Swing图形用户界面编程相关类/接口,这些类可用于构建平台无关的GUI程序

构造器的重载

同一个类里具有多个构造器,多个构造器的形参列表不同

多态

java引用变量有两个类型:编译时类型、运行时类型。

编译时类型由声明该变量时使用的类型决定,运行时的类型由实际赋给该变量的对象决定

示例代码:

/**
* Description:
* <br/>Copyright (C), 2008
*/

class BaseClass
{
public int book = 6;
public void base()
{
System.out.println("父类的普通方法");
}
public double test()
{
System.out.println("父类的被覆盖的方法");
return 0;
}
}
public class SubClass extends BaseClass
{
//重新定义一个book实例属性覆盖父类的book实例属性
//public String book = "轻量级J2EE企业应用实战";
public int book = 22;
public double test()
{
System.out.println("子类的覆盖父类的方法");
System.out.println("父类的实例属性:" + super.book);
return 0;
}
public void sub()
{
System.out.println("子类的普通方法");
}
public static void main(String[] args)
{
>//下面编译时类型和运行时类型完全一样,因此不存在多态
BaseClass bc = new BaseClass();
//输出 6
System.out.println(bc.book);
//下面两次调用将执行BaseClass的方法
bc.base();
bc.test();
//下面编译时类型和运行时类型完全一样,因此不存在多态
SubClass sc = new SubClass();
//输出"轻量级J2EE企业应用实战"
System.out.println(sc.book);
//下面调用将执行从父类继承到的base方法
sc.base();
//下面调用将执行从当前类的test方法
sc.test();
//下面调用将执行从当前类的sub方法
sc.sub();
>//下面编译时类型和运行时类型不一样,多态发生
BaseClass ploymophicBc = new SubClass();
//输出 6 —— 表明访问的是父类属性
System.out.println(ploymophicBc.book);
//下面调用将执行从父类继承到的base方法
ploymophicBc.base();
//下面调用将执行从当前类的test方法
ploymophicBc.test();
//因为ploymophicBc的编译类型是BaseClass,BaseClass类没有提供sub方法
//所以下面代码编译时会出现错误。
//ploymophicBc.sub();
}
}

Alt text

初始化块、静态初始化块、构造器

/**
* Description:
* <br/>Program Name:
* <br/>Date:
* @version 1.0
*/

class Root
{
static{
System.out.println("Root的静态初始化块");
}
{
System.out.println("Root的普通初始化块");
}
public Root()
{
System.out.println("Root的无参数的构造器");
}
}
class Mid extends Root
{
static{
System.out.println("Mid的静态初始化块");
}
{
System.out.println("Mid的普通初始化块");
}
public Mid()
{
System.out.println("Mid的无参数的构造器");
}
public Mid(String msg)
{
//通过this调用同一类中重载的构造器
this();
System.out.println("Mid的带参数构造器,其参数值:" + msg);
}
}
class Leaf extends Mid
{
static{
System.out.println("Leaf的静态初始化块");
}
{
System.out.println("Leaf的普通初始化块");
}
public Leaf()
{
//通过super调用父类中有一个字符串参数的构造器
super("Struts2权威指南");
System.out.println("执行Leaf的构造器");
}

}

public class Test
{
public static void main(String[] args)
{
new Leaf();
new Leaf();

}
}

运行结果如下:

Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块

Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:Struts2权威指南
Leaf的普通初始化块
执行Leaf的构造器


Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:Struts2权威指南
Leaf的普通初始化块
执行Leaf的构造器


上面javat程序的执行过程为:

类初始化:先执行最顶层父类的静态初始化块,依次向下,最后执行当前静态初始化块
对象初始化:先执行最顶层父类的初始化块、构造器、依次向下,最后执行当前类初始化块、构造器

单例类:

如果一个类始终只能创建一个实例,同这个类被称为单例类。 
为了避免其他类自由创建该类的实例,我们把该类的构造器使用private修饰,从而把类的所有构造器隐藏起来。

class Singleton
{
//使用一个变量来缓存曾经创建的实例
private static Singleton instance;
//将构造器使用private修饰,隐藏该构造器
private Singleton(){}
//提供一个静态方法,用于返回Singleton实例
//该方法可以加入自定义的控制,保证只产生一个Singleton对象
public static Singleton getInstance()
{
//如果instance为null,表明还不曾创建Singleton对象
//如果instance不为null,则表明已经创建了Singleton对象,将不会执行该方法
if (instance == null)
{
//创建一个Singleton对象,并将其缓存起来
instance = new Singleton();
}
return instance;
}
}
public class TestSingleton
{
public static void main(String[] args)
{
//创建Singleton对象不能通过构造器,只能通过getInstance方法
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
//将输出true
System.out.println(s1 == s2);
}
}

final修饰符

final关键可用于修饰类、变量和方法,final关键字有点类似C#里的sealed关键字,它用于表示它修饰的类、方法和变量不可改变。 
final修饰变量时,表示该变量一旦获得了初始值之后就不可被改变,final既可修饰成员变量,也可修饰局部变量、形参

final类

final修饰的类不可以有子类

不可变类

不可变类的意思是创建该类的实例后,该实例的属性是不可变的。

java提供的8个包装类和java.lang.String都是不可变类,当创建它们的实例后,其实例属性不可改变。

定义一个不可变类的方法:

属性都使用private隐藏起来,并使用final修饰这两个属性,不允许其他方法修改这两个属性值

Alt text

抽象方法和抽象类

抽象方法和抽象类必须使用abstract修饰符来定义,有抽象方法的类被只能被定义成抽象类,抽象类里可以没有抽象方法

1. 抽象类必须使用abstract修饰符来修饰,抽象方法也必须使用abstract修饰符来修饰,抽象方法不能有方法体
2. 抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例,即使抽象类里不包含抽象方法,这个抽象类不能创建实例
3. 抽象类可以包含属性、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类、枚举类六种成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用
4. 含有抽象方法的类(包括直接定义了一个抽象方法;继承了一个抽象父类,但没有完全实现父类包含的抽象方法;以及实现了一个接口,但没有完全实现接口包含的抽象方法三种情况)只能被定义成抽象类
  1. abstract 不能用于修饰属性,不能用于修饰局部变量,即没有抽象变量、没有抽象属性等说法;也不能用于修饰构造器,没有抽象构造器。
  2. abstract方法不能定义为private访问权限,即private和abstract不能同时使用

抽象类不能创建实例,只能被当成父类来继承 
抽象类是从多个具体类中抽象出来的父类,它具有更高层次的抽象

更彻底的抽象:接口

接口:不能包含普通方法,所有方法都是抽象方法

接口的定义

[修饰符] interface 接口名 extends 父接口1,父接口2…

{

零个到多个常量定义。。

零个到多个抽象方法定义。。。

}

  1. 修饰符可以是public或者省略,如果省略了public访问控制符,则默认采用包权限访问控制符,即只有在相同包结构下才可以访问该接口
  2. 一个接口可以有多个直接父接口,但接口只能继承接口,不能继承类。

    接口定义的是一种规范,因此接口里不能包含构造器和初始化定义。接口里可以包含属性(只能是常量)、方法(只能是抽象实例方法)、内部类(包括内部接口)和枚举定义。可以省略访问控制符,如果指定访问控制修饰符,只能使用public访问控制符

    接口里定义的常量属性,它们是接口相关的,而且它们只能是常量,因此系统会自动为这些属性增加statict和final两个修饰符。因些接口里的属性总将使用public、static、final修饰符

    接口里的方法,它们只能是抽象方法,因此系统会自动为其增加abstract修饰符;由于接口里的都是抽象方法,因此接口里不允许定义静态方法,即不可使用static修饰接口里定义方法,不管定义接口就去时是否使用了public abstract修饰符,接口里的方法总是使用public abstract

    实现接口方法时,必须使用public访问控制修饰符,因为接口里的方法都是public的,而子类(相当于实现类)重写父类方法时访问权限只能更大或相等,所以实现类实现接口里的方法时只能使用public访问控制权限

示例代码:

package testAbstract;

public class MathTest {

public MathTest() {
// TODO Auto-generated constructor stub
}

}
interface Jia{
int jia(int a,int b);
}

interface Jian{
int jian(int a,int b);
}

interface Cheng{
int cheng(int a,int b);
}

interface Chu{
int chu(int a,int b);
}
package testAbstract;

public class MTest implements Jia,Jian,Cheng,Chu {

public MTest() {
// TODO Auto-generated constructor stub
}

@Override
public int chu(int a, int b) {
// TODO Auto-generated method stub
return a/b;
}

@Override
public int cheng(int a, int b) {
// TODO Auto-generated method stub
return a*b;
}

@Override
public int jian(int a, int b) {
// TODO Auto-generated method stub
return a-b;
}

@Override
public int jia(int a, int b) {
// TODO Auto-generated method stub
return a+b;
}
public static void main(String[] args){
MTest mt = new MTest();
System.out.println("使用引用对象调用方法");
System.out.println("a+b = " + mt.jia(15, 3));
System.out.println("a-b = " + mt.jian(15, 3));
System.out.println("a*b = " + mt.cheng(15, 3));
System.out.println("a*b = " + mt.chu(15, 3));
/**
* mt实现了四个接口,因此可以做为变量赋值给四个接口类型
*/
Jia mJia = mt;
System.out.println("使用接口类型引用调用");
System.out.println("a+b = " + mJia.jia(15, 3));

Jian mJian = mt;
System.out.println("a-b = " + mJian.jian(15, 3));

Cheng mCheng = mt;
System.out.println("a*b = " + mCheng.cheng(15, 3));

Chu mChu = mt;
System.out.println("a*b = " + mChu.chu(15, 3));


}

}

运行结果:

Alt text

接口和抽象类在用法上的差别:

  1. 接口里只能包含抽象方法,不包含已经提供实现的方法;抽象类则完全可以包含普通方法
  2. 接口里不能定义静态方法;抽象类里可以定义静态方法
  3. 接口里只能定义静态常量属性,不能定义普通属性;抽象类里则既可以定义普通属性,也可以定义静态常量属性。
  4. 接口不包含构造器;抽象类里可以包含构造器;抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作
  5. 接口里不能包含初始化块,但抽象类则完全可以包含初始化块
  6. 一个类最多只能有一个父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多个接口可以弥补java单继承的不足。

面象接口编程

接口作为系统与外界交互的窗口,接口体现的是一种规范。对于接口的实现而言,接口规定了实现者必须向外提供哪些服务;对于接口的调用者而

言,接口规定了调用者可以调用哪些服务,以及如何调用这些服务(就是如何来调用方法)。当在一个程序中使用接口时,接口是多个模块间的耦

合标准。当在多个应用程序间使用接口时,接口是多个程序之间的通信标准。

从某种程序上来看,接口类型类似于整个系统的“总纲”,它制定了系统各模块应遵循的标准,因此一个系统的接口不应该经常改变。一旦接口改变,对整个系统甚至其他系统的影响是辐射式的,导致系统中大部分类都需要改写。

内部类

把一个类放在另一个类的内部定义,这个定义在其他类内部的类被称为内部类(有的地方也叫嵌套类),包含内部类的类也被称为外部类 
内部类有以下作用:

  1. 内部类提供了更好的封装,可以把内部类隐藏在外部之内,不允许同一个包中的其他类访问该类
  2. 内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成员之间可以相互访问。但外部不能访问内部类的实现细节,例如内部类的成员变量。

内部类比外部类可以多使用三个修饰符:private、protected、static–外部类不可以使用这三个修饰符

非静态内部类不能拥有静态成员

Alt text

/**
* 目的:测试内部类
* 日期:2015/12/13
* @author Administrator
*
*/

public class Outter {
private String outstring = "外部字符串";
public Outter() {
}
public void userInner(){
Inner a = new Inner();
a.show();
}

class Inner{
public void show(){
System.out.println(outstring);
}
}
public static void main(String[] args){
Outter outter = new Outter();
outter.userInner();
//创建内部类的实例形式如下:
Outter.Inner inner = outter.new Inner();
inner.show();
}

}

非静态内部类

定义内部类只要把一个类放在另一类内部定义即可。大部分时候,内部类都被作为成员内部类定义,而不是作为局部内部类(定义在方法中)。 
成员内部类分两种:静态内部类和非静态内部类

静态成员不能访问非静态成员,外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例等。 
java不允许非静态内部类里定义静态成员。

静态内部类

如果使用static来修饰一个内部类,这个内部类就属于外部类本身,不属于外部类的某个对象。 
练习代码:

Alt text

Alt text

Alt text

内部类

内部类适合创建那种只需要使用一次的类,创建内部类时会立即创建一个该类的实例,这个类定义立即消失,内部类不能重复使用

new 实现接口()|父类构造器(实参列表) 

//内部类的类体部分 
}

内部类必须继承一个父类,或实现一个接口,但最多只能继承一个父类,或实现一个接口

内部类有如下规则:

  1. 内部类不能是抽象类,因为系统在创建内部类时,会立即创建内部类的对象,因此不允许将内部类定义成抽象类
  2. 内部类不能定义构造器。由于内部类没有类名,所以无法定义构造器,但内部类可以定义初始化块,可以通过实例初始化块来完成构造器需要完成的事情

最常用的创建内部类的方式是需要创建某个接口类型的对象

Lambda表达式

Lambda表达式的主要作用就是代替内部类的烦琐语法。它由三部分组成:

1. 形参列表。形参列表允许省略形参类型。如果形参列表中只有一个参数,甚至连形参列表的圆括号也可以省略

2. 箭头(->)。必须通过英文中减号和大于符号组成

3. 代码块。如果代码块只包含一条语句,Lambda表达式允许省略代码块的花括号,那么这条语句就不要用花括号表示语句结束。
Lambda代码块只有一条return语句,甚至可以省略return关键字。Lambda表达式需要返回值,而它的代码块中仅有一条省略了
return的语句,Lambda表达式会自动返回这条语句的值。

Lambda表达式与函数式接口

Lambda表达式的类型,也被称为“目标类型(target type)”,Lambda表达式的目标类型必须是“函数式接口”。

函数式接口代表只包含一个抽象方法的接口。Lambda表达式只能实现一个方法,因此只能为只有一个抽象方法的接口(函数式接口)创建对象

函数式接口可以包含多个默认方法、类方法,但只能声明一个抽象方法。

由于Lambda表达式的结果就是被当成对象,因此程序完全可以使用Lambda表达式进行赋值

方法引用与构造器引用

如果lambda表达式的代码块只有一条代码,程序就可以省略lambda表达式中代码中的花括号。不仅如此,如果只有一条代码,还可以在代码块中使用方法引用和构造器引用来使表达式更简洁。 
lambda表达式支持的方法和构造器引用

各类 示例 说明 对应的lambda表达式
引用类方法 类名:类方法 函数式接口中被实现方法的全部参数传给该类方法作为参数 (a,b,…)->类名.类方法(a,b,..)
引用特定对象的实例方法 特定对象::实例方法 函数式接口中被实现方法的全部参数传给该方法作为参数 ((a,b,…)->特定对象.实例方法(a,b,…)
引用某类对象的实例方法 类名:实例方法 函数式接口中被实现方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数 (a,b,…)->a.实例方法(a,b,…)
引用构造器 类名::new 函数式接口中被实现方法的全部参数传给该构造器作为参数 (a,b,…)->new 类名(a,b,…)

枚举类

java5新增了enum关键字,与class,interface关键字的地位相同,用以定位枚举类。 
一个java源文件中最多只能定义一个public访问权限的枚举类,且该java源文件也必须和该枚举类的类名相同

  • 枚举类可以实现一个或多个 接口,使用enum定义的枚举默认继承了java.lang.Enum类,而不是默认继承Object类,因此枚举类不能显式继承其他父类。
  • 使用enum定义、非抽象的枚举类默认会使用final修饰,因此枚举类不能派生子类。
  • 枚举类的构造器只能使用private访问控制符,如果省略了构造器的访问控制符,则默认使用private修饰;如果强制使用访问控制符,只能指定private修饰符。
  • 枚举类的所有实例必须在枚举类的第一行显式列出,否则这个枚举类永远都不能产生实例。 
    枚举类默认提供了一个values()方法,该方法可以很方便的遍历所有的枚举值
    public enum SeasonEnum{
//在第一行列出4个枚举值
SPRING,SUMMER,FALL,WINTER
}

如果需要使用枚举类的某个实例,则可使用EnumClass.variable的形式,如SeasonEnum.SPRING

异常

Alt text

Alt text
finally块用于回收在try块打开的物理资源,异常机制会保证finally块总被执行除非通过exit(0)退出进程。

异常声明:
    package com.test.main;
/**
* 目的:测试异常声明
* 日期:2015/12/19
* @author Administrator
*
*/

public class TestTry {

public TestTry() {
// TODO Auto-generated constructor stub
}
public void test1(){
try{
int a = 1/0;

}catch(ArithmeticException a){
System.out.println("test1 开始运行:");

System.out.println("ArithmeticException error happened!");

System.out.println(a.getMessage());
return;
}
finally {
System.out.println("finally!");
}
System.err.println("test1 结束运行:");
}

public static void main (String[] arg){
System.out.println("main开始运行");
TestTry testTry = new TestTry();
testTry.test1();
System.out.println("main结束运行");
}
}
手动抛出异常
package com.test.main;
/**
* 目的:测试手动抛出异常
* 日期:2015/12/19
*/

import java.util.Random;

public class TestThrow {

public TestThrow() {
// TODO Auto-generated constructor stub
}

public void test() throws Exception{
throw new Exception("这是手动抛出的异常");
}
public static void main(String[] args) {
TestThrow testThrow = new TestThrow();
try {
testThrow.test();
} catch (Exception e) {
// TODO Auto-generated catch block
//e.printStackTrace();
System.out.println(e.getMessage());
}

}
}
定义自己的异常

Alt text 
自己定义的异常一般用来throw 
try不能单独出现,后面必须跟着catch或者finally,或者两者都有

package com.test.main;

/**
* 目的:测试自己定义的异常类
* 日期:2015/12/19
*
*/

public class TestException {

public TestException() {
// TODO Auto-generated constructor stub
}

public static void main(String[] args){
MyException me = new MyException("自己的异常");
System.out.println(me.getMessage());
System.out.println(me.toString());


try{
ageLevel(555);
}catch(MyException me1){
System.out.println(me1.getMessage());
System.out.println(me1.getStackTrace());
}



}
static String ageLevel(int age) throws MyException{
if(age>= 10 && age < 18){
return "少年";
}else if(age>18 && age<30){
return "青年";
}else if (age>30 && age<=60){
return "中年";
}else if(age>60 && age<120){
return "老年";
}
else{
throw new MyException("输入年龄不正确!!!");
}

}


}
class MyException extends Exception {
public MyException(){
}
public MyException(String msg){
super(msg);
}


}

Java的线程模型

Alt text

创建线程

Alt text

Alt text

Thread示例代码: 
MyThread.java

package com.test.main;

public class MyThread extends Thread {
private String name;
public MyThread() {
// TODO Auto-generated constructor stub
}
public MyThread(String arg){
super(arg);
name = arg;

}

public void run() {

for (int i = 0; i < 30; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("MyThread" + name);
}
}
}

ThreadDemo.java

package com.test.main;

public class TheadDemo {

public TheadDemo() {
// TODO Auto-generated constructor stub
}
public static void main(String[] args){
MyThread mt1 = new MyThread("线程1");
// mt.run();
mt1.start();
MyThread mt2 = new MyThread("线程2");
mt2.start();
MyThread mt3 = new MyThread("线程3");
mt3.start();
MyThread mt4 = new MyThread("线程4");
mt4.start();

for(int i = 0;i<30;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("主线程" + i);
}

System.out.println("程序执行完毕");
}

}

Runnable示例代码: 
testRunnable.java:

package com.test.main;
public class testRunnable implements Runnable{
public testRunnable() {
// TODO Auto-generated constructor stub
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i = 0;i<10;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("My thread" + i);
}
}
}

RunnableDemo.java

package com.test.main;

public class RunnableDemo {
public RunnableDemo() {
// TODO Auto-generated constructor stub
}
public static void main(String[] args) {
testRunnable rt = new testRunnable();
Thread t = new Thread(rt);
t.start();
Thread t1 = new Thread(rt);
t1.start();
Thread t2 = new Thread(rt);
t2.start();
for(int i = 0;i<10;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("主线程");

}
}
}

1. 多个线程并发执行 
java对于线程启动后唯一能保证的是每个线程都被启动并且结束。但是对于哪个先执行、哪个后执行,什么时候执行,都是CPU调度决定的,分到时间片就能执行 
2. 线程优先级 
java中优先级高的线程有更大的可能性获得cpu时间片,但不是优先级高的总是先执行,也不是优先级低的线程总不执行。

3. 线程调试的三个方法 
1 休眠方法sleep(毫秒数) sleep(毫秒数,纳秒数) 
2 暂停方法yield() a.yield() 经常被ykgd 
3 挂起方法jion() a.join

  • 17
    点赞
  • 90
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值