一.基础
*Java语言的运行机制
.java
源文件
--
编译
--> .class
字节码文件
–
在
JVM
中解释执行
-->
真正的机器指令
先编译,后解释,
JVM
事实上是一个软件
,
这个软件为
Java
程序模拟出一个统一的运行环境。
Java
程序只 需要适应这个虚拟的环境,而与底层真正的硬件环境及操作系统环境无关。换句话说,
JVM
的作用在于,它屏蔽了底层不同平台的差异。
*JRE与JDK
JRE
,是
Java Runtime Environment
的缩写,指的是
Java
的运行环境。
JDK
,是
Java Development Kit
,
Java
开发工具包。
JDK = JRE +
工具(编译器、调试器、其他工具„„)
+
类库
*环境变量
JAVA_HOME
环境变量,表示的是
Java
的安装目录。
PATH
环境变量。
CLASSPATH
是用来指示编译器和
JVM
去哪个目录寻找
.class
文件。
一个
.java
文件中定义的 每一个类,编译后都会对应的生成一个和类名完全一样的
.class
文件。
这个
.class
文件不是可执行文件,用文本编辑器也无法正常打开。
*八种基本类型
分别为
byte
、
short
、
int
、
long
、
float
、
double
、
char
、
boolean
。
*Java
自动类型提升的规则如下:
1.
如果运算数中存在
double
,则自动类型提升为
double
2.
如果运算数中没有
double
但存在
float
,则自动类型提升为
float
3.如果运算数中没有浮点类型,但存在long,则自动类型提升为long
4.其他所有情况,自动类型提升为int。
二,对象、类、封装继承多态
*对象创建过程
1.
分配空间,计算机中的对象,本质上是内存中的一块数据。因此,在计算机中创
建一个对象,就意味着必须在内存中为一个对象分配一块数据区域。 此外,在分配空间的同时,会把对象所有的属性设为默认值。这也是为什么实例变量不
用赋值,就有默认值的原因。
2.
初始化属性,
如果在定义实例变量的同时,为实例变量进行了赋值。则赋值语句在这一步骤执行。
3.
调用构造方法,当分配空间和初始化属性完成之后,最后一步是对该对象调用构造方法。
*有了继承关系之后,对象创建过程如下:
1.分配空间。要注意的是,分配空间不光是指分配子类的空间,子类对象中包含的父
类对象所需要的空间,一样在这一步统一分配。在分配空间的时候,会把所有的属性值都设为默认值。
2.递归的构造父类对象。
3.初始化本类属性。
4.调用本类的构造方法。
*
super
关键字
用法一:
super
用在构造方法上
class Child extends Parent{
public Child(String str){
super(str);
System.out.println("Child(String)");
}
}
用法二:
super
用作引用
class Parent{
public void m(){
System.out.println(“m in
Parent
”);
}
}
class Child extends Parent{
public void m1 (){
this.m();
}
public void m2(){
super.m();
}
}
*多态语法特性
(
一个引用的引用类型和对象类型未必完全一致,对象类型可以是引用类型的子类。
当我们看到一个引用时,一方面要考察这个引用的引用类型,另一方面还要考察这个 引用中所存储的对象的类型,这两个类型可能是不同的。
)
对象类型永远不变
只能对一个引用调用其引用类型中定义的方法
运行时,根据对象类型调用子类覆盖之后的方法
(
“覆盖”的定义为:子类用特殊的方法实现替换掉父类的一般的实现。
)
*继承间类型转换
子类的引用可以直接赋值给父类引用。(这是多态的基本用法)
父类的引用赋值给子类引用,必须强制类型转换,并有可能在运行时得到一个类型
转换异常。
*
instanceof
避免类型转换异常的发生,是
一个二元运算符,用来组成一个布尔表达式。用来判断某个引用所指向的对象是否和某个类型兼容。基本语法:
引用
instanceof
类名
*多态的作用
把多态用在方法的参数类型上,
在定义方法时,可以把形参定义为父类类型的引用,而调用方法时,完全可以把子类类型的对象作为实参。很显然, 形参为父类类型的方法更加通用,能够接受更多种不同子类类型的实参对象。
把多态用在方法的返回值类型上,把返回值类型定义为父类,会使得方法更加的通用。在方法的实现中,我们可以返回任何一个子类的对象。
三.static、final、abstract
在非静态方法中,无论方法或属性是否是静态的,都能够访问;
在静态方法中,只能访问静态属性和方法。
静态方法只能被静态方法覆盖,非静态方法只能被非静态方法覆盖。
静态方法可以用类名直接调用,没有多态。
类加载就是把
.class
文件读入
JVM
的过程。当
JVM
第一次遇到某个类时, 会通过
CLASSPATH
找到相应的
.class
文件,读入这个文件并把类的信息保存起来,这个过程叫做类加载。
类加载完毕之后,创建对象时,会调用对象的构造方法。
类加载除了读取类的信息,执行静态初始化代码块之外,还会为类的静态属性分配空间,并初始化其值为默认值。
final变量,用
final
修饰的变量则称为常量,一旦赋值, 其值不能改变的变量。
由于
static
属性是在类加载的时候分配空间的,因此静态的
final
属性不能 在构造方法中赋值。可以选择在定义这个属性的时候赋值,或是在静态初始代码块中为这个属性赋值。
class MyValue6{
final
static
int value = 200;
}
final方法,
表示该方法不能被子类覆盖。
class Super{
public void m1(){}
public final void m2(){}
}
class Sub extends Super {
public void m1(){} //
能够覆盖父类方法
public void m2(){} //
编译出错!无法覆盖父类方法!
}
final类,
表示这个类不能被继承。
final class MyClass{
}
class Sub extends MyClass{} // 编译出错,无法继承一个final 类
*抽象方法的语法特征
1.
抽象方法只有声明,没有实现。实现的部分用分号表示。
2.
一个拥有抽象方法的类必须是抽象类。
3.
子类继承抽象类,要么也成为抽象类,要么就必须实现抽象类中的所有抽象方法。
“覆盖”的定义为:子类覆盖父类的抽象方法时,并不是用一个特殊的实现替换一个一般的实现,而是在父类没有方法实现的情况下,子类给出一个方法的实现。
四.接口
*接口的特点
1
、 所有属性都是公开静态常量
2
、 所有方法都是公开抽象方法
interface MyInterface{
public static final int VALUE1 = 100;
public static final int VALUE2 = 200;
public abstract void m1();
public abstract void m2(int n);
},同
interface MyInterface{
int VALUE1 = 100;
int VALUE2 = 200;
void m1();
void m2(int n);
}
注意:
1.一个类实现接口,如果不希望这个类作为抽象类,则应该实现接口中定义的所有方法。
2.接口中所有的方法都是公开方法。因此,在实现接口中的方法时,实现类的方法也
必须写成公开的。
接口和接口之间可以多继承;一个类在继承一个父类的同时,还能够实现多个接口。
*接口的作用
多继承,
用接口实现的多继承,则不会破坏类之间树状结构的简单性。 这是因为这棵树是由类之间形成的,是事物主要类型所组成的关系。一个类实现再多的接口, 有再多的次要类型,也不会改变其主要类型之间的树状结构。
解耦合,
定义一个接口,可以看作是定义了一个标准。它只定义了,一个对象应该具有哪些方法,而丝毫没有定义对象如何实现这些方法。方法的实现统统交给接口的实现类来完成。这样,接口的出现,就阻隔了接口使用者和接口实现者之间的耦合关系。当接口实现者变化的时候,对接口使用者不产生任何影响。
interface Bulb{
void shine();
}
class RedBulb
implements Bulb
{
public void shine(){
System.out.println(
“
Shine in Red
”
);
}
}
class YellowBulb
implements Bulb
{
public void shine(){
System.out.println(
“
Shine in Yellow
”
);
}
}
class GreenBulb
implements Bulb
{
public void shine(){
System.out.println(
“
Shine in Green
”
);
}
}
class Lamp{
private Bulb bulb;
public void setBulb(
Bulb bulb
){
this.bulb = bulb;
}
public void on(){
bulb.shine();
}
}
public class TestLamp{
public static void main(String args[]){
Lamp lamp = new Lamp();
Bulb b1 = new RedBulb();
lamp.setBulb(b1);
lamp.on();
Bulb b2 = new GreenBulb();
lamp.setBulb(b2);
lamp.on();
}
}
Lamp
类的
bulb
属性被改为接口类型
Bulb
,从而,
Lamp
类与具体的实现类之间用
Bulb
接口分离开了。当
Lamp
类希望把
bulb
属性由
RedBulb
对象变更为
GreenBulb
对象时,不需要修改任何自身代码。
*接口回调
例如,为了定义排序规则,
Sun
公司定义了一个接口:
java.lang.Comparable
。这个接口 用来表示一个对象的排序规则。我们编写的类应该实现这个接口。
Comparable
接口中只有一个方法:
compareTo
方法,实现这个方法,就能规定两个对象如何比较大小。假设程序员要创建一个
Student
类,希望用
java.util. Arrays.sort()
对一些
Student
对象进行排序,则要求
Student
类实现
Comparable
接口。示意图如下:
class Student implements Comparable<Student>{//泛型
int age;
String name;
public int compareTo(Student stu){
//
...
}
}
由系统为我们提供
A
类和
I
接口,我们负责编写
B
类来实现
I
接口。
A
类通过对
I
接口中方法的调用,利用多态,来调用我们所写的
B
类的方法。
五.Object类与常用类、内部类
*Object类
-
finalize,
在对象被垃圾回收的时候调用,
System.gc(),
调用这个方法,就相当于通知
JVM
,程序员希望能够进行垃圾回收。
-getClass,
返回对象的实际类型。
-equals,
判断两个对象内容是否相等。
-toString,
方法的返回值是某个对象的字符串表现形式。
覆盖
equals
方法,样例
public boolean equals(Object obj){
if (obj == this) return true;
//
判断
obj
是否和
this
相等,保证自反性
if (obj == null) return false;
//
判断
obj
是否为
null
,保证最后一条准则
//
判断两个对象的实际类型是否相等,
//
如果不相等,则说明比较的是两个不同种类的对象,应当返回
false
if (obj.getClass() != this.getClass()) return false;
//
强制类型转换
//
由于之前已经使用
getClass
判断过实际类型,因此这里强转是安全的
Student stu = (Student) obj;
//
判断每个属性是否相等
//
对于基本类型的属性用“
==
”比较,对象类型的属性用
equals
比较
if (this.age == stu.age && this.name.equals(stu.name) )
return true;
else return false;
}
*包装类
int->Integer,
int i1 = 10;
Integer ii1 = new Integer(i1);
Integer->int,
int i2 = ii1.intValue();
String->包装类,
String str =
“
123
”
;
Integer ii2 = new Integer(str);
包装类->String,
String str2 = ii2.toString();
int->String,
int i = 10;
String str3 = String.valueOf(i);或
String str4 =
“”
+ i;
String->int,
int i = Integer.parseInt(
“
123
”
);
另,
Integer myInteger = 10;
int i = myInteger;
以下代码运行时会产生
NullPointerException异常
public class TestAutoBoxing {
public static void main(String[] args) {
Integer myInteger = null;
int i = myInteger;
}
}
*内部类:
成员内部类、静态内部类、局部内部类、匿名内部类
-成员内部类,
必须与外部类某一个对象相关联,因此成员内部类中不能定义静态方法。
-
静态内部类,
成员内部类中不能定义静态方法;成员内部类中能够访问外部类的所有静态以及非静态的成员;
静态内部类中可以定义静态方法,静态内部类中只能访问外部类的静态成员(即访问外部类的静态属性或者调用外部类的静态方法。)
-局部
内部类,
在局部内部类中能够访问外部类的局部变量,但是要求该变量必须是
final
的。
interface Teacher{
void teach();
}
public class TestTeacher{
public static void main(String args[]){
Teacher t = getTeacher(10);
t.teach();
}
public static Teacher getTeacher(int n)
{
class Tom implements Teacher{
public void teach(){
System.out.println(
“
Tom teach
”
);
}
}
class Jim implements Teacher{
public void teach(){
System.out.println(
“
Jim teach
”
);
}
}
if (n == 10)
return new Tom();
else return new Jim();
}
}
public class TestTeacher{
public static void main(String args[]){
Teacher t = getTeacher(10);
t.teach();
}
public static Teacher getTeacher(int n){
if (n == 10) return new Tom();
else return new Jim();
}
//反例,强耦合
public static void main(String args[]){
Teacher t = new Tom();
t.teach();
}
}
在
getTeacher
方法的外部,无法访问到
Tom
类和
Jim
类,也就无法直接创建出
Teacher
接口的实现类。也就是说,程序员只能通过调用
getTeacher
方法来获得
Teacher
对象, 而无法直接创建某个实现类的对象。这样,就能够实现“强制弱耦合”,即强制程序员必须要利用
Teacher
接口来写程序,从而实现弱耦合。
-匿名
内部类,
是一种特殊的局部内部类。
一个局部内部类满足这样两个特点:
1
、该 内部类继承自某个类或者实现某个接口;
2
、该内部类在整个方法中只创建了一个对象。
如,
public static Teacher getTeacher(int n){
class Jim implements Teacher{
//Jim
依然采用局部内部类的写法
public void teach(){
System.out.println(
“
Jim teach
”
);
}
}
//
匿名内部类
if (n == 10) return new Teacher(){
public void teach(){
System.out.println(
“
Tom teach
”
);
}
};
else return new Jim();
}
上面这段代码,把
Tom
类修改成了一个匿名内部类。首先,
return
语句返回了一个
Teacher
类型的对象。由于
Teacher
是一个接口,不能创建对象,因此所谓的
new Teacher()
,实际上是创建了一个实现
Teacher
接口的类的对象。
那创建的对象是什么类型的呢?这个实现类没有名字,这也就是为什么这种语法要叫做 “匿名”内部类的原因。而且正因为这个类没有名字,因此没有办法通过
new
类名
()
的方式创建对象。也就是说,匿名内部类没有名字,并且只能在一次方法调用中创建一个匿名内部类的对象。
new
接口名
() {
实现接口的代码
} ;
以上的代码首先用一对
{}
来实现了接口,然后用
new
关键字创建出了一个对象。这句话创建的绝不是接口的对象(接口是特殊的抽象类,无法创建对象),而创建的是一个实现了接口的,没有名字的内部类的对象。
六.集合框架
要掌握每种集合接口,就要重点掌握集合接口的这几个方面:
1
、 接口的特点
2
、 接口中定义的基本操作
3 、 该集合如何遍历
3 、 该集合如何遍历
4
、 接口的不同实现类,以及实现类之间的区别
*
Collection
-
特点是元素是
Object
。遇到基本类型数据,需要转换为包装类对象。
-基本操作
boolean add(Object o)
boolean contains(Object o)
boolean isEmpty()
Iterator iterator()
boolean remove(Object o)
void clear()
int size()
*
List
-
特点:元素是对象,并且元素有顺序,可以重复。
元素的所谓“顺序”,指的是每个元素都有下标。
-基本操作
boolean add(Object o) / void add(int index, Object element)
Object get(int index) / Object set(int index, Object element)
int indexOf(Object o)等
-遍历:for、迭代遍历,如
public class TestArrayList {
public static void main(String args[]){
List list = new ArrayList();
list.add("hello");
list.add("world");
list.add("java");
list.add("study");
Iterator iter = list.iterator();
while(iter.hasNext()){
Object value = iter.next();
System.out.println(value);
}
}
}
-实现类:Array、LinkedList、Vector
*
Set
-
特点:
元素不可以重复,无顺序。
元素没有下标的概念。
-基本操作,参考Collection
-遍历:只能迭代遍历,参考上述
Set set = new HashSet();
-实现类,HashSet、LinkedHashSet
++HashSet,
HashSet
只有在
hashCode
返回值冲突的时候才会调用
equals
方法进行判断。也就是说,两个对象,如果
hashCode
没有冲突,
HashSet
就不会调用
equals
方法判断而直接认为这两个对象是不同的对象。
如果要正常使用
HashSet
存放对象,为了保证对象的内容不重复,则要求这个对象满足:
1.覆盖
equals
方法。要求相同的对象,调用
equals
方法返回
true
。
2.覆盖
hashCode
方法。要求相同对象的
hashCode
相同,不同对象的
hashCode
尽
量不同。
++
LinkedHashSet
,按先后顺序加入元素。
*
Map
-
特点:
元素是“键值对”,
键不可以重复,值可以重复
。
-基本操作,
Object get(Object key)
put(Object key, Object value)
remove(Object key)
Set keySet()
Collection values()
containsKey / containsValue
size()
isEmpty()
clear()
entrySet
-遍历:用实现类HashMap举例,
import java.util.*;
public class TestMap {
public static void main(String args[]){
Map map = new HashMap();
map.put("2006", "Italy");
map.put("2002", "Brazil");
map.put("1998", "France");
map.put("1994", "Brazil");
}
}
++
键遍历与键值遍历
Set set = map.keySet();
Iterator iter = set.iterator();
while(iter.hasNext()){
System.out.println(iter.next());
}
//
Set set = map.keySet();
Iterator iter = set.iterator();
while(iter.hasNext()){
Object key = iter.next();
Object value = map.get(key);
System.out.println(key + "--->" + value);
}
++值
遍历
Collection conn = map.values();
Iterator iter = conn.iterator();
while(iter.hasNext()){
System.out.println(iter.next());
}
++
利用
Map.Entry
进行遍历
Set set = map.entrySet();
Iterator iter = set.iterator();
while(iter.hasNext()){
Map.Entry entry = (Map.Entry) iter.next();
System.out.println(entry.getKey() + "-->" + entry.getValue());
}
-实现类,HashMap、LinkedHashMap
*
Comparable与排序
-
在
java.util
包中,有一个
Collections
类,
类中所有的方法都是静态方法,如
Collections.sort
方法。
-
TreeSet
与
TreeMap
++
Set
接口有一个子接口:
SortedSet
,对应的实现类:
TreeSet
。
++Map
接口有一个子接口:
SortedMap
,对应的实现类:
TreeMap
。
++
要求放入
TreeSet
接口中的对象都必须实现
Comparable
接口。
如,
class Student implements Comparable<Student>{
int age;
String name;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public int compareTo(Student stu){
if (this.age > stu.age){
return 1;
}else if (this.age < stu.age){
return -1;
}else {
return 0;
}
}
}
public class TestTreeSet {
public static void main(String args[]) {
Set set = new TreeSet();
set.add(new Student("Tom", 18));
set.add(new Student("Jim", 17));
set.add(new Student("Jerry", 20));
Iterator iter = set.iterator();
while(iter.hasNext()){
Student stu = (Student) iter.next();
System.out.println(stu.name + " " + stu.age);
}
}
}
*
5.0新特性:foreach循环
String[] ss = new String[]{“hello”, “
world
”, “java”};
for(String obj : ss){
System.out.println(obj);
}
*
5.0新特性:泛型
-定义,
List
接口定义 为:
List<E>
,其中,
E
就表示
List
的泛型。
-泛型与多态,
类型可以有多态,泛型不能够有多态,
不同泛型的引用之间
不能相互赋值。
List<Dog> dogList = new ArrayList<Dog>();
List<Animal> aniList = dogList; //! 编译出错!
在第一行代码中
,类型里有多态(把
ArrayList
赋值给
List
),但是泛型是一样的(均是
Dog
的 泛型)。
在第二行代码中
,类型中没有多态(
List
是相同的),而泛型有多态(一个是
Dog
的泛型,一个 是
Animal
的泛型)。
-自定义泛型化类型
class Pair<T>{
private T valueA;
private T valueB;
public T getValueA() {
return valueA;
}
public void setValueA(T valueA) {
this.valueA = valueA;
}
public T getValueB() {
return valueB;
}
public void setValueB(T valueB) {
this.valueB = valueB;
}
}
public class TestPair {
public static void main(String[] args){
Pair<Dog> p = new Pair<Dog>();
p.setValueA(new Dog());
p.setValueB(new Dog());
p.setValueA(new Cat()); //
编译出错!
}
}
七.异常处理
*异常分类
|-Throwable,所有错误的父类
|-Error,严重的底层错误,无法处理
|-Exception,异常,异常处理的主要对象
|-RuntimeException的子类,未检查异常,可以避免,可处理可不处理
|-非RuntimeException的子类,已检查异常,无法避免,必须处理
import java.util.*;
import java.io.*;
import java.sql.*;
import java.sql.*;
public class TestException{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
System.out.println("main 1");
ma(i);
System.out.println("main 2");
}
static void ma(int i) {
try{
System.out.println("ma 1");
mb(i);
System.out.println("ma 2");
}catch(IOException ioe){
System.out.println(
“
IOException
”
);
}catch(SQLException sqle){
System.out.println(
“
SQLException
”
);
}catch(Exception e){
System.out.println(
“
Exception
”
);
}
finally{
System.out.println("in finally of ma");
}
}
static void mb(int i) throws IOException, SQLException{
System.out.println("mb 1");
mc(i);
System.out.println("mb 2");
}
static void mc(int i) throws FileNotFoundException,
EOFException, SQLException{
System.out.println("mc 1");
if (i==0) throw new NullPointerException();
if (i==1) throw new java.io.FileNotFoundException();
if (i==2) throw new java.io.EOFException();
if (i==3) throw new java.sql.SQLException();
System.out.println("mc 2");
}
}
*throw,表明抛出一个错误,语法throw+Throwable对象,如
throw new NullPointerException();
*throws,声明抛出异常,上述代码
*try-catch,捕获异常
*finally,无论程序执行时是否发生异常,最终都会被执行
*异常与方法覆盖,
子类的覆盖方法不能比父类的被覆盖方法抛出更多的异常。
*Exception类getMessage方法,返回该方法的详细信息。
*Exception类printStackTrace方法,
在标准错误输出上打印出产生异常时的方法调用栈的信息。
如,
import java.util.*;
import java.io.*;
import java.sql.*;
public class TestExceptionArgs{
public class TestExceptionArgs{
public static void main(String args[]){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
ma(n);
}
public static void ma(int n) {
try{
mb(n);
}catch(Exception e){
e.printStackTrace();
System.out.println(e.getMessage());
}
}
public static void mb(int n) throws Exception{
if (n == 0) throw new SQLException("n==0");
if (n == 1) throw new EOFException("n==1");
if (n == 2) throw new FileNotFoundException("n==2");
}
}
*自定义异常
class MyException1 extends Exception{
//
自定义已检查异常
public MyException1(){}
public MyException1(String str){
super(str);
}
}
class MyException2 extends RuntimeException{
//
自定义未检查异常
public MyException2(){}
public MyException2(String str){
super(str);
}
}
public class TestMyException{
public static void main(String args[]){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
ma(n);
}
public static void ma(int n) {
try{
mb(n);
}catch(MyException1 e){
e.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}
}
public static void mb(int n) throws MyException1{
if (n == 0) throw new MyException1();
if (n == 1) throw new MyException2();
}
}
八.多线程
*基本常识
-
Java
代码是运行在
JVM
中的, 对于某个操作系统来说,一个
JVM
就相当于一个进程。而
Java
代码不能够越过
JVM
直接与操作系统打交道,因此,
Java
语言中没有多进程的概念。
Java
中的并发,采用的是线程的概念。简单的来说,一个操作系统可以同时运行多个程序,也就是说,一个系统并发多个进程;而对于每个进程来说,可以同时运行多个线程,也 就是:一个进程并发多个线程。--
宏观上并行,微观上串行
-
线程运行的条件:
CPU
、代码、数据,数据在
堆空间共享,栈空间独立。
*两种代码实现方式,Thread类、Runnable接口
class MyThread1 extends Thread{
public void run(){
for(int i = 1; i<=1000; i++){
System.out.println(i + " $$$");
}
}
}
class MyRunnable2 implements Runnable{
public void run(){
for(int i = 1; i<=1000; i++){
System.out.println(i + " ###");
}
}
}
public class TestThread{
public static void main(String args[]){
Thread t1 = new MyThread1();
Runnable target = new MyRunnable2();
Thread t2 = new Thread(target);
t1.start();
t2.start();
}
}
*线程状态
初始状态:创建一个线程对象,而没有调用这个线程对象的
start()
方法时,此时线程处于初始状态。
可运行状态 :线程已经为运行做好了完全准备,只等着获得
CPU
来运行。
调用
start()
方法之,进入此状态。
运行状态:处于这种状态的线程获得了
CPU
时间片,正在执行代码。
终止状态:当一个线程执行完了
run()
方法中的代码,该线程就会进入终止状态。
阻塞状态:
如果线程需要与
JVM
外部进行数据交互(如等待用户输入、读写文件、网络传输等,最典型的是
等待I/O
),当数据传输没有完成时,线程即使获得
CPU
也无法运行,因此进入阻塞状态。
++sleep
方法签名
public static void sleep(long millis) throws InterruptedException
应用样例
class MyThread1 extends Thread{
public void run(){
for(int i = 1; i<=1000; i++){
System.out.println(i + " $$$");
try{
Thread.sleep(200);
}catch(InterruptedException e){}
}
}
}
++join
方法签名
public final void join() throws InterruptedException
public final void join(long millis) throws InterruptedException
应用样例
class MyThread1 extends Thread{
public void run(){
for(int i = 0; i<100; i++){
System.out.println(i + " $$$");
}
}
}
class MyThread2 extends Thread{
Thread t;
public void run(){
try{
t.join(1000);
}catch(Exception e){}
for(int i = 0; i<100; i++){
System.out.println(i + " ###");
}
}
}
public class TestJoin{
public static void main(String args[]){
MyThread1 t1 = new MyThread1();
MyThread2 t2 = new MyThread2();
t2.t = t1;
t1.start();
t2.start();
}
}
锁池状态:
运行中的线程,运行到某个同步代码块,但获得不了对象的锁标记时,进入锁池状态。在锁池状态的线程,会一直等待某个对象的互斥锁标记。
当对象的锁标记被某一个线程释放之后,其他在锁池状态中的线程就可以获得这个对象的锁标记。
之后进入可运行状态,等待获得
CPU
时间片,运行代码。
++synchronized与同步方法
样例1
class MyStack{
char[] data = {'A', 'B', ' '};
int index = 2;
private Object lock = new Object();
public void push(char ch){
synchronized(lock){
data[index] = ch;
try{
Thread.sleep(1000);
}catch(Exception e){}
index ++;
}
}
public void pop(){
synchronized(lock){
index --;
data[index] = ' ';
}
}
public void print(){
for(int i = 0; i<data.length; i++){
System.out.print(data[i] + "\t");
}
System.out.println();
}
}
样例2,
class MyStack{
char[] data = {'A', 'B', ' '};
int index = 2;
public void push(char ch){
synchronized(this){ //类对象本身,具有互斥锁标记
data[index] = ch;
try{
Thread.sleep(1000);
}catch(Exception e){}
index ++;
}
}
public void pop(){
synchronized(this){
index --;
data[index] = ' ';
}
}
public void print(){
for(int i = 0; i<data.length; i++){
System.out.print(data[i] + "\t");
}
System.out.println();
}
}
样例3,
class MyStack{
char[] data = {'A', 'B', ' '};
int index = 2;
public
synchronized
void push(char ch){//同步方法
data[index] = ch;
try{
Thread.sleep(1000);
}catch(Exception e){}
index ++;
}
public
synchronized
void pop(){
index --;
data[index] = ' ';
}
public void print(){
for(int i = 0; i<data.length; i++){
System.out.print(data[i] + "\t");
}
System.out.println();
}
}
锁池状态:
t1
线程调用
a
对象的
wait
方法,
t1
线程会暂时释放自己拥有的
a
对象的锁标记,进入等待状态。(
调用对象
wait
方法,前提是线程已经获得这个对象的锁标记,反之
会产生异常。
)
t2线程
调用
a对象的notify/notifyAll
方法,会让t1线程被唤醒,进入锁池状态。
样例代码,生产者/消费者问题
class MyStack{
private char[] data = new char[5];
private int index = 0;
public char pop(){
index -- ;
return data[index];
}
public void push(char ch){
data[index] = ch;
index++;
}
public void print(){
for (int i=0; i<index; i++){
System.out.print(data[i] + "\t");
}
System.out.println(); }
public boolean isEmpty(){
return index == 0;
}
public boolean isFull(){
return index == 5;
}
}
class Consumer extends Thread{
private MyStack ms;
public Consumer(MyStack ms) {
this.ms = ms;
}
public void run(){
while(true){
//
为了保证
push
和
pop
操作的完整性,
必须加
synchronized
synchronized(ms){
//
如果栈空间空,则
wait()
释放
ms
的锁标记
while(ms.isEmpty()){
try {
ms.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
char ch = ms.pop();
System.out.println("Pop " + ch);
ms.notifyAll();
}
//push
之后随机休眠一段时间
try {
sleep( (int)Math.abs(Math.random() * 100) );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//
生产者
class Producer extends Thread{
private MyStack ms;
public Producer(MyStack ms) {
this.ms = ms;
}
public void run(){
while(true){
//
为了保证
push
和
pop
操作的完整性,
必须加
synchronized
synchronized(ms){
//
如果栈空间已满,则
wait()
释放
ms
的锁标记
while(ms.isFull()){
try {
ms.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
ms.push('A');
System.out.println("push A");
ms.notifyAll();
}
//push
之后随机休眠一段时间
try {
sleep( (int)Math.abs(Math.random() * 200) );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class TestWaitNotify {
public static void main(String[] args) {
MyStack ms = new MyStack();
Thread t1 = new Producer(ms);
Thread t2 = new Consumer(ms);
t1.start();
t2.start();
}
}
九.IO框架
*I/O分类
按方向分类,分为输入流和输出流
按数据单位分类,分为字节流和字符流
按功能分类,分为节点流和过滤流
InputStream<-FileInputStream,OutputStream<-FileOutputStream
import java.io.*;
public class TestInputStream{
public static void main(String args[])
throws Exception{
FileInputStream fin = new FileInputStream("abc.txt");
int ch = 0;
while( (ch=fin.read()) != -1){
System.out.print((char)ch);
}
fin.close();
}
}
import java.io.*;
public class TestInputStream{
public static void main(String args[]) throws Exception{
FileInputStream fin = new FileInputStream("abc.txt");
byte[] bs = new byte[6];
int len = 0;
while( (len=fin.read(bs))!=-1){
for(int i = 0; I < len; i++){
System.out.print((char)bs[i]);
}
System.out.println();
}
fin.close();
}
}
import java.io.*;
public class TestInputStream{
public static void main(String args[]) {
FileInputStream fin = null;
try{
fin = new FileInputStream("abc.txt");
byte[] bs = new byte[6];
int len = 0;
while( (len=fin.read(bs))!=-1){
for(int i = 0; i<len; i++){
System.out.print((char)bs[i]);
}
System.out.println();
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(fin!=null)
try{
fin.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
}
DataInputStream,DataOutputStream
import java.io.*;
public class TestDataStream{
public static void main(String args[]) throws Exception{
//1.
创建节点流
FileOutputStream fout = new FileOutputStream("pi.dat");
//2.
封装过滤流
DataOutputStream dout = new DataOutputStream(fout);
//3.
写数据
dout.writeDouble(3.14);
//4.
关闭外层流
dout.close();
//
创建节点流
FileInputStream fin= new FileInputStream ("pi.dat");
//
封装过滤流
DataInputStream din = new DataInputStream(fin);
//
读数据
double pi = din.readDouble();
//
关闭外层流
din.close();
System.out.println(pi);
}
}
BufferedInputStream,
BufferedOutputStream
import java.io.*;
public class TestBufferedStream{
public static void main(String args[]) throws Exception{
String data = "Hello World";
byte[] bs = data.getBytes();
//1.
创建节点流
FileOutputStream fout
= new FileOutputStream("test.txt");
//2.
封装过滤流
BufferedOutputStream bout
= new BufferedOutputStream(fout);
//3.
写数据
bout.write(bs);
//4.
关闭外层流
bout.close();
}
}
序列化与transient
import java.io.*;
class Student implements Serializable{
String name;
transient int age;//
不参与序列化
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
public class TestSerializable {
public static void main(String[] args) throws Exception {
Student stu1 = new Student("tom", 18);
Student stu2 = new Student("jerry", 18);
FileOutputStream fout = new FileOutputStream("stu.dat");
ObjectOutputStream oout = new ObjectOutputStream(fout);
oout.writeObject(stu1);
oout.writeObject(stu2);
oout.close();
FileInputStream fin = new FileInputStream("stu.dat");
ObjectInputStream oin = new ObjectInputStream(fin);
Student s1 = (Student) oin.readObject();
Student s2 = (Student) oin.readObject();
oin.close();
System.out.println(s1.name + " " + s1.age);
System.out.println(s2.name + " " + s2.age);
}
}
Reader,FileReader,FileWriter
Reader<-InputStreamReader,Reader<-OutputStreamWriter
通过桥转换获得字符流,也是一个获得字符流的方式。这种方式有两个用法:
1
、如果需要指定编码方式,则应当使用桥转换。
2
、在无法直接获得字符流的情况下,可以先获得字节流,再通过桥转换获得字符流。
利用桥转换进行编程,需要以下五个步骤:
1
、创建节点流
2
、桥转换为字符流
3
、在字符流的基础上封装过滤流
4
、读
/
写数据
5
、关闭外层流
BufferedReader,
PrintWriter
import java.io.*;
public class TestPoem {
public static void main(String[] args) throws Exception {
//
创建节点流
FileInputStream fin = new FileInputStream("poem.txt");
//
桥转换
Reader r = new InputStreamReader(fin, "GBK");
//
封装过滤流
BufferedReader br = new BufferedReader(r);
//
读
/
写数据
String line = null;
while( (line=br.readLine()) !=null){
System.out.println(line);
}
//
关闭外层流
br.close();
}
}
import java.io.*;
public class TestPrintWriter {
public static void main(String[] args) throws Exception {
FileOutputStream fout
= new FileOutputStream("poem2.txt");
Writer w = new OutputStreamWriter(fout, "GBK");
PrintWriter pw = new PrintWriter(w);
pw.println("
一个人在清华园
");
pw.println("
我写的
Java
程序
");
pw.println("
是全天下
");
pw.println("
最面向对象的
");
pw.close();
}
}
十.反射
*获取类对象
-类名.class,如Class c = int.class;
-getClass()方法,
通过类的对象获得类对象。
-Class.forName()方法,静态方法,签名如下,(已检查异常),会触发类加载
public static Class forName(String className) throws ClassNotFoundException
样例代码,
public static void main(String args[]) throws Exception{
Class c = Class.forName("p1.p2.TestClass");
}
*使用类对象获取类的信息
getName():
获得类的名称,包括包名;
getSimpleName():
获得类的名称,不包括包名;
getSuperClass():
获得本类的父类的类对象;
getInterfaces() :
获得本类所实现的所有接口的类对象,返回值类型为
Class[]。
import java.util.ArrayList;
public class TestClass1 {
public static void main(String[] args) {
Class c = ArrayList.class;
String className = c.getName();
System.out.println
("
类名:
"+
className
);
String simpleName = c.getSimpleName();
System.out.println
("
简单类名:
"+
simpleName
) ;
Class superClass = c.getSuperclass();
System.out.println
(
"
父类:
"+
superClass
.getName ()) ;
Class[] interfaces = c.getInterfaces();
System.out.println
(
"
接口:
");
for(int i =0 ; i < interfaces.length ; i++){
System.out.println(interfaces[i].getName());
}
}
}
*使用类对象获取类中方法的信息
public Method[] getDeclaredMethods() throws SecurityException
public Method[] getMethods() throws SecurityException
getDeclaredMethods,
返回在本类中定义的所有方法,包括私有方法,
不能获得父类中的任何方法。
getMethods,
包括所有的公开方法,也包括父类中定义的公开方法,
私有方法不会被获取。
均抛出未检查异常,可处理可不处理。
样例代码,
public static void printMethod(Object obj){
//
获取
obj
对象所对应的类对象
Class c = obj.getClass();
//
通过类对象,获取其中的所有方法对象
Method[] ms = c.getMethods();
//
打印每个方法的方法名
for(int i = 0 ; i < ms.length ; i++){
System.out.println(ms.getName());
}
}
*使用类对象创建类的对象
public class Student {
public String name;
private int age;
public Student(){
System.out.println("Student()");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void study(){
System.out.println(name + " study");
}
public void study(int h){
System.out.println(name + " study for " + h + " hours");
}
public double study(int a, double b){
System.out.println(name + " study " + a + " " + b);
return a * b;
}
private void play(){
System.out.println(name + " play");
}
}
import java.lang.reflect.*;
public class TestReflection {
public static void main(String[] args) throws Exception {
Class c = Student.class;
Student stu = (Student) c.newInstance();
}
}
public static Object createObject(String className){
Object result = null;
try{
Class c = Class.forName(className);
result = c.newInstance();
}catch(Exception e){
e.printStackTrace();
}
return result;
}
*Field类
Field getDeclaredField(String name)
Field getField(String name)
public void set(Object obj, Object value)
public Object get(Object obj)
public void setAccessible(boolean flag)
样例代码,
Student stu = new Student();
Class c = stu.getClass();
Field nameField = c.getField("name"); //1.
哪一个对象的属性
nameField.set(stu,
"tom"); //2.
对象的哪一个属性,3
对象的属性值修改成什么
Class c = stu.getClass();
Field f = c.getDeclaredField("name");
Object value = f.get(stu);//要读取哪一对象的属性
Field f = stu.getClass().getDeclaredField("age");
f.setAccessible(true);
f.set(stu, new Integer(18) );//修改私有属性
*Method类
public Method getMethod(String name,Class[] parameterTypes)
public Method getDeclaredMethod(String name,Class[] parameterTypes)
与
getMethods,
getDeclaredMethods类似,
第一个参数是一个字符串参数,表示的是方法的方法名。
第二个参数是方法的参数表,
无参方法,参数表就是一个空数组:
new Class[]{}
Class c = Student.class;
Method m = c.getMethod("study",
new Class[]{int.class,double.class});
样例代码(利用反射调用study方法),
Student stu = new Student();
Class c = stu.getClass();
//1
哪个对象调用方法
Method m = c.getDeclaredMethod("study", new Class[]{int.class, double.class});
//2,调用对象的哪个方法;
//3,传入实参;
//4,方法可以有返回值
Object result
= m.invoke(stu,
new Object[]{new Integer(10), new Double(1.5) } );
其中,
public Object invoke(Object obj, Object[] args)
1
、第一个参数
obj
表示对哪一个对象调用方法
2
、第二个参数表示调用方法时的参数表
3
、
invoke
方法的返回值对应于
Method
对象所代表的方法的返回值。
4
、调用私有方法前,先调用
setAccessible(true)。
*Constructor类
getConstructors/
getDeclaredConstructors
1,可以通过
Class
类中的
getConstructors() / getDeclaredConstructors()
获得
Constructor
数组。
2,可以通过
Class
类中的
getConstructor() / getDeclaredConstructor()
来获得指定的构 造方法。
这两个方法只有一个参数:一个
Class
数组。
3,可以调用
Constructor
类中的
newInstance
方法创建对象。创建对象的时候,会调用相应的构造方法。
注:
如果创建对象只需要调用无参构造方法的话,就可以直接使用
Class
类中的
newInstance
方法,如果在创建对象的时候需要指定调用其他构造方法的话,就需要使用
Constructor
类。
样例代码,
import java.lang.reflect.*;
class Dog{
String name;
int age;
public Dog(){
System.out.println("Dog()");
}
public Dog(String name, int age) {
System.out.println("Dog(String, int)");
this.name = name;
this.age = age;
}
public String toString(){
return name + " " + age;
}
}
public class TestConstructor {
public static void main(String[] args) throws Exception {
Class c = Dog.class;
//无参构造方法
Dog d1 = (Dog) c.newInstance();
System.out.println(d1);
//
获得构造方法
Constructor con = c.getConstructor(
new Class[]{String.class, int.class});
//
创建对象时指定构造方法
//
为构造方法传递的参数
Dog d2 = (Dog) con.newInstance(
new Object[]{"Snoopy", new Integer(5)});
System.out.println(d2);
}
}
*反射的作用
创建
Student
对象,并调用
study()
方法的代码。
String className = "Student";
Class c = Class.forName(className);
Object o = c.newInstance();
String methodName = "study";
Method m = c.getMethod(methodName , new Class[]{});
m.invoke(o , new Object[]{});
反射技术有着非常显著的几个缺点。
1,运行效率与不用反射的代码相比会有明显下降。
2,代码的复杂度大幅提升,这个从代码量上大家就能比较出来
3,代码会变得脆弱,不易调试。使用反射,我们就在一定程度上绕开了编译器的语法
检查,例如,用反射去调用一个对象的方法,而该对象没有这个方法,那么在编译时,编译器是无法发现的,只能到运行时由JVM抛出异常。
十一.OOAD初步
*依赖、聚合与组合
依赖关系,又被称为
use-a
关系。
A
类中有一个
B
类型的局部变量(当然,方法参数也可以认为是特殊的局部变量)。
class Person{
public void crossRiver(Boat b){
...
}
}
关联关系,指的是
A
类中有一个
B
类型的属性。
人“有”自行车,在这种关系中,人和自行车是相对比较独立的对象。这个独立,指的
是外部对象“人”和内部对象“自行车”的生命周期之间,没有必然的联系,
称之为“聚合”。
外部对象一旦不存在,则内部对象也一定不存在了,即外部对象管理内部对象的生命周期,
称之为“组合”。
关联关系的方向性与多重性
*单例模式
public class Singleton{
private Singleton(){}
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
}
写单例模式的时候,有三个要点:
1
、私有的构造方法;
2
、 静态的
instance
属性;
3
、静态的
getInstance()
方法。
*设计原则
开闭原则:
对扩展开放,对修改关闭。
实现开闭原则,软件应该具备以下特点:
可重用性
可扩展性
弱耦合性
可扩展性
弱耦合性
各司其职
*三层体系结构:数据访问层、业务逻辑层、数据显示层
//构造接口
//Dao.java
package dao;
public interface Dao {
String getData();
}
//Biz.java
package biz;
import dao.Dao;
public interface Biz {
void setDao(Dao dao);
String dealData();
}
//View.java
package view;
import biz.Biz;
public interface View {
void setBiz(Biz biz);
void showData();
}
//实现接口
//FileDaoImpl.java
package dao;
import java.io.*;
public class FileDaoImpl implements Dao {//数据访问层
public String getData() {
String data = null;
BufferedReader br = null;
try{
br = new BufferedReader(new FileReader("test.txt"));
data = br.readLine();
}catch(IOException e){
e.printStackTrace();
}
finally{
if (br != null){
try
{
br.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
return data;
}
}
//UpperCaseImpl.java
package biz;
import dao.Dao;
public class UpperCaseBizImpl implements Biz {//业务处理层,实现转大写
private Dao dao;
public String dealData() {
String data = dao.getData();
if (data != null){
data = data.toUpperCase();
}
return data;
}
public void setDao(Dao dao) {
this.dao = dao;
}
}
//TextViewImpl.java
package view;
import biz.Biz;
public class TextViewImpl implements View {//数据显示层
private Biz biz;
public void setBiz(Biz biz) {
this.biz = biz;
}
public void showData() {
String data = biz.dealData();
System.out.println(data);
}
}
//简单工厂模式应用+配置文件+反射,SimpleFactory.java
package factory;
import dao.*;
import biz.*;
import view.*;
import biz.*;
import view.*;
import java.util.Properties;
import java.io.*;
public class SimpleFactory {//工厂模式,对象创建过程独立
private Properties props;
public SimpleFactory(){
props = new Properties();
InputStream is = null;
try{
is = new FileInputStream("conf.props");
props.load(is);
}catch(IOException e){
e.printStackTrace();
}
finally{
if (is!=null){
try
{
is.close();
}
catch(Exception e){
e.printStackTrace();
}
}
}
}
public Dao createDao(){
String className = props.getProperty("dao");
Dao dao = (Dao) createObject(className);
return dao;
}
public Biz createBiz(){
String className = props.getProperty("biz");
Biz biz = (Biz) createObject(className);
return biz;
}
public View createView(){
String className = props.getProperty("view");
View view = (View) createObject(className);
return view;
}
private Object createObject(String name){
Object result = null;
try {
Class c = Class.forName(name);
result = c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
//主程序,Test.java
package test;
import dao.*;
import biz.*;
import view.*;
import biz.*;
import view.*;
import factory.SimpleFactory;
public class Test {
public static void main(String[] args) {
SimpleFactory factory = new SimpleFactory();
Dao dao = factory.createDao();
Biz biz = factory.createBiz();
biz.setDao(dao);
View view = factory.createView();
view.setBiz(biz);
view.showData();
}
}
其中,配置文件conf.props内容格式如下,
view=view.TextViewImpl
biz=biz.UpperCaseBizImpl
dao=dao.FileDaoImpl
在
Java
中,有一个非常方便的类:
java.util.Properties,
是
Hashtable
的子类,它是一个特殊的
Map。这个类的
getProperty
方法,接受字符串类型的参数,表示“键”;返回值也是字符串,对应的是值。
这个类的
load
方法,读入配置文件,
可以接受一个
InputStream
参数。如果要读文件的话,可以创建一个
FileInputStream
作为
load
方法参数,
load
方法会自动解析输入流,如
conf.props文件
,会被自动解析出三个键值对放入
Properies
中,键分别为“
view
”、“
biz
”、“
dao
”,对应的值为“
view.TextViewImpl
”、 “
biz.UpperCaseBizImpl
”、“
dao.FileD aoImpl
”。
当有新需求,希望把所有字符改成小写,只需要修改配置文件:
biz=biz.LowerCaseBizImpl
//新的实现类如下,
package biz;
import dao.Dao;
public class LowerCaseBizImpl implements Biz {
public class LowerCaseBizImpl implements Biz {
private Dao dao;
public String dealData() {
String data = dao.getData();
if (data != null){
data = data.toLowerCase();
}
return data;
}
public void setDao(Dao dao) {
this.dao = dao;
}
}
整个应用的类图如下,
编译与运行
javac -d . Dao.java FileDaoImpl.java
javac -d . Biz.java UpperCaseBizImpl.java
javac -d . View.java TextViewImpl.java
javac -d . SimpleFactory.java
javac -d . Test.java
java test.Test