void clearerr(FILE *); int fclose(FILE *); int feof(FILE *); int ferror(FILE *); int fflush(FILE *); int fgetc(FILE *); int fgetpos(FILE *, fpos_t *); char *fgets(char *, int, FILE *); FILE *fopen(const char *, const char *); int fprintf(FILE *, const char *, ...); int fputc(int, FILE *); int fputs(const char *, FILE *); size_t fread(void *buf, size_t size, size_t count, FILE *fp); FILE *freopen(const char *, const char *, FILE *); int fscanf(FILE *, const char *, ...); int fseek(FILE *, long, int); int fseeko(FILE *, off_t, int); int fsetpos(FILE *, const fpos_t *); long ftell(FILE *); off_t ftello(FILE *); size_t fwrite(const void *buf, size_t size, size_t count, FILE *fp); int getc(FILE *); int getchar(void); char *gets(char *);
const int MaxLine =1000; // 值不可修改 int r=6; int * const pr=&r; // 指针pr不可修改,但*pr可修改 const int *p; // 指针p可修改,但不可以通过指针对变量重新赋值,即不能对*p赋值 int x; const int *const px=&x // px和*px都不可修改
1) 一维数组:
基本数据类型 数组名[ ] ; // 声明一维数组
数组名 = new 基本数据类型[个数] ; // 分配内存给数组
基本数据类型 数组名[] = {初值0,初值1,…,初值n}
数组元素个数:数组名.length
System.arrayCopy(source,0,dest,0,x):语句的意思就是:复制源数组从下标0开始的x个元素到目标数组,从目标数组的下标0所对应的位置开始保存
Arrays.sort(数组名)为数组排序的操作,但这个方法在java.util这个包里面
2) 二维数组
基本数据类型 数组名[ ][ ] ;
数组名 = new 基本数据类型[行的个数][列的个数] ;
基本数据类型 数组名[ ][ ] = { {第0行初值},
{第1行初值},
…
{第n行初值},
};
每行的元素个数不同的二维数组:
int num[ ][ ] = {
{42,54,34,67},
{33,34,56},
{12,34,56,78,90}
};
行数:数组名.length // 取得数组的行数
列数:数组名[行的索引].length // 取得特定行元素的个数
二维数组作为参数或返回值:
1) 形参:基本数据类型 数组名[ ][ ]
2) 实参:数组名
3) 对象数组
Person p[ ] ; // 声明Person类类型的数组变量
p = new Person[3] ; // 用new分配内存空间
p[0] = new Person () ; //用new产生新的对象,并分配内存空间给它
p[1] = new Person () ; //用new产生新的对象,并分配内存空间给它
p[2] = new Person () ; //用new产生新的对象,并分配内存空间给它
或
Person p[] = {new Person(),new Person(),new Person()} ;
创建对象:类名 对象名 = new 类名() ;
访问属性:对象名称.属性名
访问方法:对象名称.方法名()
封装属性:private 属性类型 属性名
封装方法:private 方法返回类型 方法名称(参数)
“匿名对象”,顾名思义,就是没有明确的声明的对象。读者也可以简单的理解为只使用一次的对象,即没有任何一个具体的对象名称引用它。(如:new 类名.方法())
有两种方式可用于对象间的比较,它们是“= =”运算符与equals()方法,“= =”操作符用于比较两个对象的内存地址值是否相等,equals()方法用于比较两个对象的内容是否一致。
“==”是比较内存地址值的,“equals”是比较内容的。
String str1 = "java" ; String str2 = new String("java") ; String str3 = "java" ;
class Person { String name ; int age ; public Person() { System.out.println("1. public Person()"); } public Person(String name,int age) { // 调用本类中无参构造方法 this() ; //必须放在首行 this.name = name ; this.age = age ; System.out.println("2. public Person(String name,int age)"); } }
1) 静态变量(类变量):
所有的对象都指向同一个static属性,只要当中有一个对象修改了static属性的内容,则所有对象都会被同时修改。
用static方式声明的属性,也可以用类名直接访问,即:类名.static属性 ;
2) 静态方法(类方法)
static可以用其来声明方法,用它声明的方法有时也被称为“类方法”,访问方法:类名.static方法;
3) 静态变量与静态方法的关系
如果在类中声明了一static类型的属性,则此属性既可以在非static类型的方法中使用,也可以在static类型的方法中使用。但用static类型的方法不能访问非static类型的属性。
4) 静态代码块
一个类可以使用不包含在任何方法体中的静态代码块,当类被载入时,静态代码块被执行,且只执行一次,静态代码块经常用来进行类属性的初始化。如下面的程序代码所示:class Person { public Person() { System.out.println("1.public Person()"); } // 此段代码会首先被执行 static { System.out.println("2.Person类的静态代码块被调用!"); } } public class TestStaticDemo5 { // 运行本程序时,静态代码块会被自动执行 static { System.out.println("3.TestStaticDemo5类的静态代码块被调用!"); } public static void main(String[] args) { System.out.println("4.程序开始执行!"); // 产生两个实例化对象 new Person() ; new Person() ; } }
执行结果如下:
3.TestStaticDemo5类的静态代码块被调用! 4.程序开始执行! 2.Person类的静态代码块被调用! 1.public Person() 1.public Person()
Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); inner.display() ;
1) 在java中只允许单继承,而不允许多重继承,也就是说一个子类只能有一个父类,但是java中却允许多层继承;
2) 子类对象在实例化时会默认先去调用父类中的无参构造方法,之后再调用本类中的相应构造方法;
3) 如果程序中指定了构造方法,则默认无参构造方法不会再生成;
4) 用super(实参列表)调用父类中的构造方法,只能放在方法的第一行
5) super也可用于调用父类中的属性或方法
super.父类中的属性 ;
super.父类中的方法() ;
6) 子类在继承父类时,会继承父类中的全部的属性与方法;但子类不能显示地访问父类中的private属性和方法。
7) 复写:
a) “复写”的概念与“重载”相似,它们均是Java“多态”的技术之一,所谓“重载”,即是方法名称相同,但却可在不同的场合做不同的事。当一个子类继承一父类,而子类中的方法与父类中的方法的名称,参数个数、类型都完全一致时,就称子类中的这个方法复写了父类中的方法。同理,如果子类中重复定义了父类中已有的属性,则称此子类中的属性复写了父类中的属性;
b) 子类对象在调用复写的方法时,实际上调用的是子类中被复写好了的方法;
c) 子类复写父类中的方法时,被子类复写的方法不能拥有比父类中更严格的访问权限;
d) 子类可以通过super.方法()的方式调用父类中被子类复写的方法;
8) this与super的比较
抽象类的作用有点类似“模版”,其目的是要设计者依据它的格式来修改并创建新的类。但是并不能直接由抽象类创建对象,只能通过抽象类派生出新的类,再由它来创建对象。
抽象类定义规则:
1) 抽象类和抽象方法都必须用abstract关键字来修饰;
2) 抽象类不能被实例化,也就是不能用new关键字去产生对象;
3) 抽象方法只需声明,而不需实现;
4) 含有抽象方法的类必须被声明为抽象类,抽象类的子类必须复写所有的抽象方法后才能被实例化,否则这个子类还是个抽象类。
5) 抽象类也可以像普通类一样,有构造方法、一般方法、属性,更重要的是还可以有一些抽象方法,留给子类去实现,而且在抽象类中声明构造方法后,在子类中必须明确调用
在Java中声明类、属性和方法时,可使用关键字final来修饰。
1) final标记的类不能被继承。
2) final标记的方法不能被子类复写。
3) final标记的变量(成员变量或局部变量)即为常量,只能赋值一次。
接口(interface)是Java所提供的另一种重要技术,它的结构和抽象类非常相似,也具有数据成员与抽象方法,但它与抽象类又有以下两点不同:
1) 接口里的数据成员必须初始化,且数据成员均为常量。
2) 接口里的方法必须全部声明为abstract,也就是说,接口不能像抽象类一样保有一般的方法,而必须全部是“抽象方法”。
接口定义的语法如下:
接口与一般类一样,本身也具有数据成员与方法,但数据成员一定要赋初值,且此值将不能再更改,方法也必须是“抽象方法”。也正因为方法必须是抽象方法,而没有一般的方法,所以抽象方法声明的关键字abstract是可以省略的。相同的情况也发生在数据成员身上,因数据成员必须赋初值,且此值不能再被更改,所以声明数据成员的关键字final也可省略。
事实上只要记得:(1)接口里的“抽象方法”只要做声明即可,而不用定义其处理的方式;(2)数据成员必须赋初值,这样就可以了。
每一个由接口实现的类必须在类内部复写接口中的抽象方法,且可自由地使用接口中的常量。
接口是java实现多继承的一种机制,一个类只能继承一个父类,但如果需要一个类继承多个抽象方法的话,就明显无法实现,所以就出现了接口的概念。一个类只可以继承一个父类,但却可以实现多个接口。
接口与一般类一样,均可通过扩展的技术来派生出新的接口。原来的接口称为基本接口或父接口,派生出的接口称为派生接口或子接口。通过这种机制,派生接口不仅可以保留父接口的成员,同时也可加入新的成员以满足实际的需要。
同样的,接口的扩展(或继承)也是通过关键字extends来实现的。有趣的是,一个接口可以继承多个接口,这点与类的继承有所不同。
1) 向上转型(实例本身是子类):
•父类对象通过子类对象去实例化,实际上就是对象的向上转型。向上转型是不需要进行强制类型转换的,但是向上转型会丢失精度。
•向上转型可以自动完成;
2) 向下转型(实例本身是子类):
•与向上转型对应的一个概念就是“向下转型”,所谓向下转型,也就是说父类的对象可以转换为子类对象,但是需要注意的是,这时则必须要进行强制的类型转换。
•向下转型必须进行强制类型转换。
调用的属性和方法为实例本身的属性和方法。
Java中有一个比较特殊的类,就是Object类,它是所有类的父类,如果一个类没有使用extends关键字明确标识继承另外一个类,那么这个类就默认继承Object类。因此,Object类是Java类层中的最高层类,是所有类的超类。换句话说,Java中任何一个类都是它的子类。由于所有的类都是由Object类衍生出来的,所以Object类中的方法适用于所有类。
package java.lang; /** * The root class of the Java class hierarchy. All non-primitive types * (including arrays) inherit either directly or indirectly from this class. */ public class Object { /** * Constructs a new instance of {@code Object}. */ public Object() { } protected Object clone() throws CloneNotSupportedException { if (!(this instanceof Cloneable)) { throw new CloneNotSupportedException("Class doesn't implement Cloneable"); } return internalClone((Cloneable) this); } /* * Native helper method for cloning. */ private native Object internalClone(Cloneable o); /** * Compares this instance with the specified object and indicates if they * are equal. In order to be equal, {@code o} must represent the same object * as this instance using a class-specific comparison. The general contract * is that this comparison should be reflexive, symmetric, and transitive. * Also, no object reference other than null is equal to null. */ public boolean equals(Object o) { return this == o; } /** * Invoked when the garbage collector has detected that this instance is no longer reachable. * The default implementation does nothing, but this method can be overridden to free resources. */ @FindBugsSuppressWarnings("FI_EMPTY") protected void finalize() throws Throwable { } /** * Returns the unique instance of {@link Class} that represents this * object's class. Note that {@code getClass()} is a special case in that it * actually returns {@code Class<? extends Foo>} where {@code Foo} is the * erasure of the type of the expression {@code getClass()} was called upon. */ public final native Class<?> getClass(); /** * Returns an integer hash code for this object. By contract, any two * objects for which {@link #equals} returns {@code true} must return * the same hash code value. This means that subclasses of {@code Object} * usually override both methods or neither method. */ public native int hashCode(); /** * Causes a thread which is waiting on this object's monitor (by means of * calling one of the {@code wait()} methods) to be woken up. If more than * one thread is waiting, one of them is chosen at the discretion of the * VM. The chosen thread will not run immediately. The thread * that called {@code notify()} has to release the object's monitor first. * Also, the chosen thread still has to compete against other threads that * try to synchronize on the same object. */ public final native void notify(); /** * Causes all threads which are waiting on this object's monitor (by means * of calling one of the {@code wait()} methods) to be woken up. The threads * will not run immediately. The thread that called {@code notify()} has to * release the object's monitor first. Also, the threads still have to * compete against other threads that try to synchronize on the same object. */ public final native void notifyAll(); /** * Returns a string containing a concise, human-readable description of this * object. Subclasses are encouraged to override this method and provide an * implementation that takes into account the object's type and data. The * default implementation is equivalent to the following expression: */ public String toString() { return getClass().getName() + '@' + Integer.toHexString(hashCode()); } /** * Causes the calling thread to wait until another thread calls the {@code * notify()} or {@code notifyAll()} method of this object. This method can * only be invoked by a thread which owns this object's monitor; see * {@link #notify()} on how a thread can become the owner of a monitor. */ public final void wait() throws InterruptedException { wait(0 ,0); } /** * Causes the calling thread to wait until another thread calls the {@code * notify()} or {@code notifyAll()} method of this object or until the * specified timeout expires. This method can only be invoked by a thread * which owns this object's monitor; see {@link #notify()} on how a thread * can become the owner of a monitor. */ public final void wait(long millis) throws InterruptedException { wait(millis, 0); } /** * Causes the calling thread to wait until another thread calls the {@code * notify()} or {@code notifyAll()} method of this object or until the * specified timeout expires. This method can only be invoked by a thread * that owns this object's monitor; see {@link #notify()} on how a thread * can become the owner of a monitor. */ public final native void wait(long millis, int nanos) throws InterruptedException; }
Object类的方法中有一个toString()方法,此方法是在打印对象时被调用的。
如: Person p = new Person();
System.out.println(p); // 实质上调用Person.toString方法,所以在Person中需要复写toString方法。它相当于:System.out.println(p.toString());
2) equals方法
Object中的equals用于比较两个对象地址是否相同;如果需要用于比较两个对象内容是否相同,则需要复写。
interface A { public void fun1() ; } class B { int i = 10 ; public void get(A a) { a.fun1() ; } public void test() { this.get(new A() //匿名内部类 { public void fun1() { System.out.println(i) ; } } ); } }
1) 通过extends关键字,可将父类的成员(包含数据成员与方法)继承到子类。
2) Java在执行子类的构造方法之前,会先调用父类中无参的构造方法,其目的是为了对继承自父类的成员做初始化的操作。
3) 父类有数个构造方法时,如要调用特定的构造方法,则可在子类的构造方法中,通过super()这个关键字来完成。
4) this()是在同一类内调用其它的构造方法,而super()则是从子类的构造方法调用其父类的构造方法。
5) this()除了可用来调用同一类内的其它构造方法之外,如果同一类内“实例变量”与局部(local)变量的名称相同时,也可利用它来调用同一类内的“实例变量”。
6) this()与super()其相似之处:(1)当构造方法有重载时,两者均会根据所给予的参数的类型与个数,正确地执行相对应的构造方法。(2)两者均必须编写在构造方法内的第一行,也正是这个原因,this()与super()无法同时存在同一个构造方法内。
7) “重载”(overloading),它是指在相同类内,定义名称相同,但参数个数或类型不同的方法,因此Java便可依据参数的个数或类型调用相应的方法。
8) “复写”(overriding),它是在子类当中,定义名称、参数个数与类型均与父类相同的方法,用以复写父类中的方法。
9) 如果父类的方法不希望子类的方法来复写它,可在父类的方法之前加上“final”关键字,如此该方法便不会被复写。
10) final的另一个功用是把它加在数据成员变量前面,如此该变量就变成了一个常量(constant),如此便无法在程序代码中再做修改了。
11) 所有的类均继承自Object类。
12) 复写Object类中的equals() method可用来比较两个类的对象是否相等。
13) Java可以创建抽象类,专门用来当做父类。抽象类的作用类似于“模板”,其目的是依据其格式来修改并创建新的类。
14) 抽象类的方法可分为两种:一种是一般的方法,另一种是以abstract关键字开头的“抽象方法”。“抽象方法”并没有定义方法体,而是要保留给由抽象类派生出的新类来定义。
15) 利用父类的变量数组来访问子类的内容的较好的做法是:
(1) 先创建父类的变量数组;
(2) 利用数组元素创建子类的对象,并以它来访问子类的内容。
16) 抽象类不能直接用来产生对象。
17) 接口的结构和抽象类非常相似,它也具有数据成员与抽象method,但它与抽象类有两点不同:
(1) 接口的数据成员必须初始化。
(2)接口里的方法必须全部都声明成abstract。
18) 利用接口的特性来打造一个新的类,称为接口的实现(implementation)。
19) Java并不允许多重继承。
20) 接口与一般类一样,均可通过扩展的技术来派生出新的接口。原来的接口称为基本接口或父接口;派生出的接口成为派生接口或子接口。通过这种机制,派生接口不仅可以保留父接口的成员,同时也可以加入新的成员以满足实际的需要。
21) Java对象的多态性分为:向上转型(自动))向下转型(强制)。
22) 通过instanceof关键字,可以判断对象属于那个类。
23) 匿名内部类(anonymous inner class)的好处是可利用内部类创建不具有名称的对象,并利用它访问到类里的成员。
几种常见的异常:
1) 算术异常(ArithmeticException);
2) 没有给对象开辟内存空间时会出现空指针异常(NullPointerException);
3) 找不到文件异常(FileNotFoundException)。
在没有异常处理的语言中,就必须使用if或switch等语句,配合所想得到的错误状况来捕捉程序里所有可能发生的错误。但为了捕捉这些错误,编写出来的程序代码经常有很多的if语句,有时候这样也未必能捕捉到所有的错误,而且这样做势必导致程序运行效率的降低。
Java的异常处理机制恰好改进了这一点。它具有易于使用、可自行定义异常类,处理抛出的异常同时又不会降低程序运行的速度等优点。因而在Java程序设计时,应充分地利用Java的异常处理机制,以增进程序的稳定性及效率。
• 值得一提的是,finally块是可以省略的。如果省略了finally块不写,则在catch()块运行结束后,程序跳到try-cath块之后继续执行。
习惯上将Error与Exception类统称为异常类,但这两者本质上还是有不同的。Error类专门用来处理严重影响程序运行的错误,可是通常程序设计者不会设计程序代码去捕捉这种错误,其原因在于即使捕捉到它,也无法给予适当的处理,如JAVA虚拟机出错就属于一种Error。
不同于Error类,Exception类包含了一般性的异常,这些异常通常在捕捉到之后便可做妥善的处理,以确保程序继续运行。
IOException一定要编写异常处理的程序代码才行,它通常用来处理与输入/输出相关的操作,如文件的访问、网络的连接等。
如:throw new ArithmeticException("一个算术异常");
2) 指定方法抛出异常
如果由方法会抛出异常,则可将处理此异常的try-catch-finally块写在调用此方法的程序代码内。此方法的定义方式如下:
方法名称(参数…) throws 异常类1,异常类2,…
如:void add(int a,int b) throws Exception { }
Exception构造方法:public Exception(String message)
1) 程序中没有处理异常代码时,Java的默认异常处理机制会做下面的操作:
(1) 抛出异常。
(2) 停止程序运行。
2) 异常处理是由try、catch与finally三个关键字所组成的程序块,其语法请参考格式7-1。
3) try程序块中若有异常发生时,程序的运行便会中断,抛出“由系统类所产生的对象”,并依下列的步骤来运行:
(1) 抛出的对象如果属于catch()括号内所要捕捉的异常类,catch会捕捉此异常,然后进到catch程序块里继续执行。
(2)无论try程序块是否捕捉到异常,也不管捕捉到的异常是否与catch()括号里的异常相同,最后都会运行finally块里的程序代码。
(3) finally中的代码是异常的统一出口,无论是否发生异常都会执行此段代码。
4) 当异常发生时,有两种处理方式:
(1)交由Java默认的异常处理机制去处理。
(2)自行编写try-catch-finally块来捕捉异常。
5) 异常可分为两大类:java.lang.Exception与java.lang.Error类。
6) RuntimeException可以不编写异常处理的程序代码,依然可以编译成功,它是在程序运行时才有可能发生的;而其它的Exception一定要编写异常处理的程序代码才能使程序通过编译。
7) catch()括号内,只接收由Throwable类的子类所产生的对象,其它的类均不接收。
8) 抛出异常有下列两种方式:
(1)在程序中抛出异常。
(2)指定方法抛出异常。
9) 程序中抛出异常时,要用到throw这个关键字。
10) 如果方法会抛出异常(使用throws),则可将处理此异常的try-catch-finally块写在调用此方法的程序代码中
编译方法:javac -d . HelloWorld.java
执行方法:java my.test.HelloWorld
若要访问不同package内某个public类的成员时,在程序代码内必须明确地指明“被访问package的名称.类名称”。其使用语法如下:
import package名称.类名称;
import package名称.*;则导入包中所有的类。在Java中,导入全部类或是导入指定的类,对于程序的性能是没有影响的,所以在开发中可以直接写导入全部类会比较方便。
SUN公司在JDK中为程序开发者提供了各种实用类,这些类按功能不同分别被放入了不同的包中,供开发者使用,下面简要介绍其中最常用的几个包:
1) java.lang — 包含一些Java语言的核心类,如String、Math、Integer、System和Thread,提供常用功能。在java.lang包中还有一个子包:java.lang.reflect用于实现java类的反射机制。
2) java.awt — 包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
3) javax.swing — 此包用于建立图形用户界面,此包中的组件相对于java.awt包而言是轻量级组件。
4) java.applet — 包含applet运行所需的一些类。
5) java.net — 包含执行与网络相关的操作的类。
6) java.io — 包含能提供多种输入/输出功能的类。
7) java.util — 包含一些实用工具类,如定义系统特性、与日期日历相关的函数。
注意:java1.2以后的版本中,java.lang这个包会自动被导入,对于其中的类,不需要使用import语句来做导入了,如前面经常使用的System类。
在JAVA中有四种访问控制权限,分别为:private、default、protected、public。
1) private访问控制符
如果一个成员方法或成员变量名前使用了private访问控制符,那么这个成员只能在这个类的内部使用。
注意:不能在方法体内声明的变量前加private修饰符。
2) default访问控制符
如果一个成员方法或成员变量名前没有使用任何访问控制符,就称这个成员所拥有的是默认的(default)访问控制符。默认的访问控制成员可以被这个包中的其它类访问。如果一个子类与其父类位于不同的包中,子类也不能访问父类中的默认访问控制成员。
3) protected访问控制符
如果一个成员方法或成员变量名前使用了protected访问控制符,那么这个成员既可以被同一个包中的其它类访问,也可以被不同包中的子类访问。
4) public访问控制符
如果一个成员方法或成员变量名前使用了public访问控制符,那么这个成员可以被所有的类访问,不管访问类与被访问类是否在同一个包中。
1) 包名:包名中的字母一律小写,如:demo.java。
2) 类名、接口名:应当使用名词,每个单词的首字母大写,如:TestPerson。
3) 方法名:第一个单词小写,后面每个单词的首字母大写,如:talkMySelf。
4) 常量名:每个字母一律大写,如:COUNTRY。
开发者用的JDK中的包和类主要在JDK的安装目录下的jre\lib\rt.jar文件中,由于Java虚拟机会自动找到这个jar包,所以在开发中使用这个jar包的类时,无需再用classpath来指向它们的位置了。
jar文件就是Java Archive File,而它的应用是与Java息息相关的。jar文件就是一种压缩文件,与常见的ZIP压缩文件格式兼容,习惯上称之为jar包。如果开发者开发了许多类,当需要把这些类提供给用户使用时,通常都会将这些类压缩到一个jar文件中,以jar包的方式提供给用户使用。只要别人的classpath环境变量的设置中包含这个jar文件,Java虚拟机就能自动在内存中解压这个jar文件,把这个jar文件当作一个目录,在这个jar文件中去寻找所需要的类及包名所对应的目录结构。
生成.jar文件:
jar –cvf create.jar demo
· -c:创建新的存档
·-v:生成详细输出到标准输出上
· -f:指定存档文件名
· create.jar:是生成jar文件的名称
· demo:要打成jar文件的包
举例如下:
在HelloWorld (jerry.test)中需要import danny.test.Person和danny.test.Student
1) javac -d . Person.java Student.java
2) jar -cvf danny.jar danny
3) set classpath=%classpath%;D:\Java\danny.jar
4) echo %classpath%
5) javac -d . HelloWorld.java
6) java jerry.test.HelloWorld
1) java中使用包可以实现多人协作的开发模式。
2) 在java中使用package关键字来将一个类放入一个包中。
3) 在java中使用import语句,可以导入一个已有的包。
4) java中的访问控制权限分为四种:private、default、protected、public。
5) 使用jar命令可以将一个包打成一个jar文件,供用户使用。
如果在类里要激活线程,必须先做好下面两个准备:
1) 线程必须扩展自Thread类,使自己成为它的子类;
2) 线程的处理必须编写在run()方法内。
3) 启动线程:new 类名称().start();
如:class MyThread extends Thread { }
new MyThread().start();
JAVA程序只允许单一继承,即一个子类只能有一个父类,所以在Java中如果一个类继承了某一个类,同时又想采用多线程技术的时,就不能用Thread类产生线程,因为Java不允许多继承,这时就要用Runnable接口来创建线程了。
如:class MyThread implements Runnable { }
MyThread p = new MyThread();
new Thread(p).start();
实现Runnable接口相对于继承Thread类来说,有如下显著的优势:
(1) 适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码、数据有效分离,较好地体现了面向对象的设计思想。
(2) 可以避免由于Java的单继承特性带来的局限。开发中经常碰到这样一种情况,即:当要将已经继承了某一个类的子类放入多线程中,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么就只能采用实现Runnable接口的方式了。
(3) 增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。当多个线程的执行代码来自同一个类的实例时,即称它们共享相同的代码。多个线程可以操作相同的数据,与它们的代码无关。当共享访问相同的对象时,即共享相同的数据。当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例。
事实上,几乎所有多线程应用都可用第二种方式,即实现Runnable接口。
对Java程序来说,只要还有一个前台线程在运行,这个进程就不会结束,如果一个进程中只有后台线程在运行,这个进程就会结束。前台线程是相对后台线程而言的,前面所介绍的线程都是前台线程。那么什么样的线程是后台线程呢?如果某个线程对象在启动(调用start()方法)之前调用了setDaemon(true)方法,这个线程就变成了后台线程。
当一个线程运行时,另一个线程可以调用对应的Thread对象的interrupt()方法来中断它。也可以用Thread对象调用isInterrupted()方法来检查每个线程的中断状态。Thread.sleep抛出中断异常之后,会清除中断标记;否则中断标记一直存在。
在同一时刻只能有一个线程可以进入同步代码块内运行,只有当该线程离开同步代码块后,其它线程才能进入同步代码块内运行。
除了可以对代码块进行同步外,也可以对函数实现同步,只要在需要同步的函数定义前加上synchronized关键字即可。
在同一类中,使用synchronized关键字定义的若干方法,可以在多个线程之间同步,当有一个线程进入了有synchronized修饰的方法时,其它线程就不能进入同一个对象使用synchronized来修饰的所有方法,直到第一个线程执行完它所进入的synchronized修饰的方法为止。
Java是通过Object类的wait、notify、notifyAll这几个方法来实现线程间的通信的,又因为所有的类都是从Object继承的,所以任何类都可以直接使用这些方法。下面是这三个方法的简要说明:
• wait:告诉当前线程放弃监视器并进入睡眠状态,直到其它线程进入同一监视器并调用notify为止。
• notify:唤醒同一对象监视器中调用wait的第一个线程。类似排队买票,一个人买完之后,后面的人可以继续买。
• notifyAll:唤醒同一对象监视器中调用wait的所有线程,具有最高优先级的线程首先被唤醒并执行。
wait、notify、notifyAll这三个方法只能在synchronized方法中调用,即无论线程调用一个对象的wait还是notify方法,该线程必须先得到该对象的锁标记,这样,notify只能唤醒同一对象监视器中调用wait的线程,使用多个对象监视器,就可以分别有多个wait、notify的情况,同组里的wait只能被同组的notify唤醒。
一个线程的等待和唤醒过程可以用下图表示:
控制线程生命周期的方法有很多种,如:suspend方法、resume方法和stop方法。但这三个方法都不推荐使用,其中,不推荐使用suspend和resume的原因是:
(1) 会导致死锁的发生。
(2) 它允许一个线程(甲)通过直接控制另外一个线程(乙)的代码来直接控制那个线程(乙)。
虽然stop能够避免死锁的发生,但是也有其它的不足:如果一个线程正在操作共享数据段,操作过程没有完成就被“stop”了的话,将会导致数据的不完整性。因此stop方法也不提倡使用了!
控制线程生命周期的可用方法如下:
(1) 通过控制run方法中循环条件的方式来结束一个线程的方法是推荐读者使用的,这也是实际中用的最多的方法。
1、线程(thread)是指程序的运行流程。“多线程”的机制可以同时运行多个程序块,使程序运行的效率更高,也解决了传统程序设计语言所无法解决的问题。
2、如果在类里要激活线程,必须先做好下面两项准备:
(1)、此类必须是扩展自Thread类,使自己成为它的子类。
(2)、线程的处理必须编写在run()方法内。
3、run()方法是定义在Thread类里的一个方法,因此把线程的程序代码编写在run()方法内,所做的就是覆盖的操作。
4、Runnable接口里声明了抽象的run()方法,因此必须在实现Runnable接口的类里明确定义run()方法。
5、每一个线程,在其创建和消亡之前,均会处于下列五种状态之一:创建、就绪、运行、阻塞、终止。
6、暂停状态的线程可由下列的情况所产生:
(1)该线程调用对象的wait()时。
(2)该线程本身调用sleep()时。
(3)该线程和另一个线程join()在一起时。
7、被冻结因素消失的原因有:
(1)如果线程是由调用对象的wait()方法所冻结,则该对象的notify()方法被调用时可解除冻结。
(2)线程进入休眠(sleep)状态,但指定的休眠时间到了。
8、当线程的run()方法运行结束,或是由线程调用它的stop()方法时,则线程进入消亡状态。
9、Thread类里的sleep()方法可用来控制线程的休眠状态,休眠的时间要视sleep()里的参数而定。
10、要强制某一线程运行,可用join()方法。
11、join()方法会抛出InterruptedException的异常,所以编写时必须把join()方法编写在try-catch块内。
12、当多个线程对象操纵同一共享资源时,要使用synchronized关键字来进行资源的同步处理。
JAVA语言定义了许多类专门负责各种方式的输入输出,这些类都被放在java.io包中。
File类是IO包中唯一代表磁盘文件本身的对象,File类定义了一些与平台无关的方法来操纵文件,通过调用File类提供的各种方法,能够完成创建、删除文件,重命名文件,判断文件的读写权限及是否存在,设置和查询文件的最近修改时间等操作。
Java 能正确处理UNIX和Windows/DOS约定路径分隔符。如果在Windows版本的Java下用斜线(/),路径处理依然正确。记住,如果使用Windows/DOS使用反斜线(\)的约定,就需要在字符串内使用它的转义序列(\\)。Java约定是用UNIX和URL风格的斜线来作路径分隔符。
下面的构造方法可以用来生成File 对象:
new File(String directoryPath)
这里,directoryPath是文件的路径名。
RandomAccessFile对象类有个位置指示器,指向当前读写处的位置,当读写n个字节后,文件指示器将指向这n个字节后的下一个字节处。刚打开文件时,文件指示器指向文件的开头处,可以移动文件指示器到新的位置,随后的读写操作将从新的位置开始。
public RandomAccessFile(File file, String mode) throws FileNotFoundException /* mode: "r":只读 "rw":读写 "rws":每次write时,同步文件和metadata "rwd":每次write时,同步文件而不同步metadata */
Java 的流式输入/输出建立在四个抽象类的基础上:InputStream, OutputStream, Reader和Writer。它们用来创建具体流式子类。尽管程序通过具体子类执行输入/输出操作,但顶层的类定义了所有流类的基本通用功能。
InputStream 和OutputStream:设计成字节流类。
Reader 和Writer :为字符流设计。
字节流类和字符流类形成分离的层次结构。一般说来,处理字符或字符串时应使用字符流类,处理字节或二进制对象时应用字节流类。
一般在操作文件流时,不管是字节流还是字符流都可以按照以下的方式进行:
1) 使用File类找到一个文件
2) 通过File类的对象去实例化字节流或字符流的子类
3) 进行字节(字符)的读、写操作
4) 关闭文件流
InputStream 是一个定义了Java流式字节输入模式的抽象类。该类的所有方法在出错条件下都会引发一个IOException 异常。下表显示了InputStream的方法:
public abstract class InputStream extends Object implements Closeable
OutputStream是定义了流式字节输出模式的抽象类。该类的所有方法返回一个void 值并且在出错情况下引发一个IOException异常。
FileInputStream 类创建一个能从文件读取字节的InputStream 类,它的两个常用的构造方法如下:
FileInputStream(String filepath)
FileInputStream(File fileObj)
FileOutputStream 创建了一个可以向文件写入字节的类OutputStream,它常用的构造方法如下:
FileOutputStream(String filePath)
FileOutputStream(File fileObj)
FileOutputStream(String filePath, boolean append)
尽管字节流提供了处理任何类型输入/输出操作的足够的功能,但它们不能直接操作Unicode字符。既然Java的一个主要目标是支持“一次编写,处处运行”,包含直接的字符输入/输出的支持是必要的。
Reader是定义Java的流式字符输入模式的抽象类。该类的所有方法在出错情况下都将引发IOException 异常。
FileReader类创建了一个可以读取文件内容的Reader类。它最常用的构造方法显示如下:
FileReader(String filePath)
FileReader(File fileObj)
每一个都能引发一个FileNotFoundException异常。这里,filePath是一个文件的完整路径,fileObj是描述该文件的File 对象。
FileWriter 创建一个可以写文件的Writer 类。它最常用的构造方法如下:
FileWriter(String filePath)
FileWriter(String filePath, boolean append)
FileWriter(File fileObj)
它们可以引发IOException或SecurityException异常。这里,filePath是文件的绝对路径,fileObj是描述该文件的File对象。如果append为true,输出是附加到文件尾的。FileWriter类的创建不依赖于文件存在与否。在创建文件之前,FileWriter将在创建对象时打开它来作为输出。如果试图打开一个只读文件,将引发一个IOException异常。
管道流主要作用是可以连接两个线程间的通信。管道流也分为字节流(PipedInputStream、PipedOutputStream)与字符流(PipedReader、PipedWriter)两种类型,下面主要讲解PipedInputStream与PipedOutputStream。
一个PipedInputStream对象必须和一个PipedOutputStream对象进行连接而产生一个通信管道,PipedOutputStream可以向管道中写入数据,PipedInputStream可以从管道中读取PipedOutputStream写入的数据。如下图所示,这两个类主要用来完成线程之间的通信,一个线程的PipedInputStream对象能够从另外一个线程的PipedOutputStream对象中读取数据。
ByteArrayInputStream是输入流的一种实现,它有两个构造函数,每个构造函数都需要一个字节数组来作为其数据源:
ByteArrayInputStream(byte[ ] buf)
ByteArrayInputStream(byte[ ] buf,int offset , int length)
ByteArrayOutputStream()
BuyteArrayoutputStream(int)
如果程序在运行过程中要产生一些临时文件,可以采用虚拟文件方式实现,JDK中提供了ByteArrayInputStream和ByteArrayOutputStream两个类可实现类似于内存虚拟文件的功能。
为了支持标准输入输出设备,Java定义了两个特殊的流对象:System.in和System.out。System.in对应键盘,是InputStream类型的,程序使用System.in可以读取从键盘上输入的数据;System.out对应显示器,是PrintStream类型的,PrintStream是OutputStream的一个子类,程序使用System.out可以将数据输出到显示器上。键盘可以被当作一个特殊的输入流,显示器可以被当作一个特殊的输出流。
PrintStream类提供了一系列的print和println方法,可以实现将基本数据类型的格式转化成字符串输出。在前面的程序中大量用到“System.out.println”语句中的System.out就是PrintStream类的一个实例对象。PrintStream有下面几个构造方法:
PrintStream(OutputStream out)
PrintStream(OutputStream out,boolean auotflush)
PrintStream(OutputStream out,boolean auotflush, String encoding)
其中autoflush控制在Java中遇到换行符(\n)时是否自动清空缓冲区,encoding是指定编码方式。println方法与print方法的区别是:前者会在打印完的内容后再多打印一个换行符(\n),所以println()等于print("\n")。
Java的PrintStream对象具有多个重载的print和println方法,它们可输出各种类型(包括Object)的数据。对于基本数据类型的数据,print和println方法会先将它们转换成字符串的形式然后再输出,而不是输出原始的字节内容,如:整数221的打印结果是字符‘2’、‘2’、‘1’所组合成的一个字符串,而不是整数221在内存中的原始字节数据。对于一个非基本数据类型的对象,print和println方法会先调用对象的toString方法,然后再输出toString方法所返回的字符串。
DataInputStream与DataOutputStream提供了与平台无关的数据操作,通常会先通过DataOutputStream按照一定的格式输出,再通过DataInputStream按照一定格式读入。由于可以得到java的各种基本类型甚至字符串,这样对得到的数据便可以方便地进行处理,这在通过协议传输的信息的网络上是非常适用的。
采用SequenceInputStream类,可以实现两个文件的合并操作。
Java支持字节流和字符流,但有时需要字节流和字符流之间的转换。
InputStreamReader和OutputStreamWriter,这两个类是字节流和字符流之间转换的类,InputStreamReader可以将一个字节流中的字节解码成字符,OuputStreamWriter将写入的字符编码成字节后写入一个字节流。
• InputStreamReader有两个主要的构造函数:
1) InputStreamReader(InputStream in)
// 用默认字符集创建一个InputStreamReader对象
2) InputStreamReader(InputStream in,String CharsetName)
// 接受已指定字符集名的字符串,并用该字符集创建对象
• OutputStreamWriter也有对应的两个主要的构造函数:
1) OutputStreamWriter(OutputStream in)
// 用默认字符集创建一个OutputStreamWriter对象
2) OutputStreamWriter(OutputStream in,String CharsetNarme)
// 接受已指定字符集名的字符串,并用该字符集创建OutputStreamWriter对象
为了达到最高的效率,避免频繁地进行字符与字节间的相互转换,最好不要直接使用这两个类来进行读写,应尽量使用BufferedWriter类包装OutputStreamWriter类,用BufferedReader类包装InputStreamReader类。例如:
BufferedWriter out=new BufferedWriter(new OutputStreamWriter(System.out));
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
所谓的对象序列化(也叫串行化),是指将对象转换成二进制数据流的一种实现手段,通过将对象序列化,可以方便的实现对象的传输及保存。
在Java中提供了ObjectInputStream与ObjectOutputStream这两个类用于序列化对象的操作。这两个类是用于存储和读取对象的输入输出流类,不难想象,只要把对象中的所有成员变量都存储起来,就等于保存了这个对象,之后从保存的对象之中再将对象读取进来就可以继续使用此对象。ObjectInputStream与ObjectOutputStream类,可以帮开发者完成保存和读取对象成员变量取值的过程,但要求读写或存储的对象必须实现了Serializable接口,但Serializable接口中没有定义任何方法,仅仅被用作一种标记,以被编译器作特殊处理。
import java.io.* ; public class Person implements Serializable { private String name ; private int age ; public Person(String name,int age) { this.name = name ; this.age = age ; } public String toString() { return " 姓名:"+this.name+",年龄:"+this.age ; } }; public class SerializableDemo { public static void main( String args[] ) throws Exception { File f = new File("SerializedPerson") ; serialize(f); deserialize(f); } // 以下方法为序列化对象方法 public static void serialize(File f) throws Exception { OutputStream outputFile = new FileOutputStream(f); ObjectOutputStream cout = new ObjectOutputStream(outputFile); cout.writeObject(new Person("张三",25)); cout.close(); } // 以下方法为反序列化对象方法 public static void deserialize(File f) throws Exception { InputStream inputFile = new FileInputStream(f); ObjectInputStream cin = new ObjectInputStream(inputFile); Person p = (Person) cin.readObject(); System.out.println(p); } }
1、 Java中要进行IO操作,需要导入java.io包。
2、 Java中的File类是唯一操作磁盘文件的类。
3、 Java中的数据操作主要分为两种:
(1)、字节流(OutputStream、InputStream)(常用:FileOutputStream和FileInputStream)
(2)、字符流(Writer、Reader) (常用:FileWriter和FileReader)
这四个类都是抽象类,使用时,都必须依靠其子类实例化。
4、 Java定义了两个特殊的流对象:System.in和System.out。System.in对应键盘,是InputStream类型的,程序使用System.in可以读取从键盘上输入的数据;System.out对应显示器,可以向显示器上输出内容。
5、 InputStreamReader和OutputStreamWriter,这两个类是字节流和字符流之间转换的类,InputStreamReader可以将一个字节流中的字节解码成字符,OuputStreamWriter将写入的字符编码成字节后写入一个字节流。
6、一个类实现了Serializable接口之后,此类的对象可以被序列化,就表示可以保存在文件之中、或网络传输之中。如果不希望类中的某个属性被保存下来,可以用transient关键字声明属性。
一个字符串就是一连串的字符,字符串的处理在许多程序中都用得到。Java定义了String和StringBuffer两个类来封装对字符串的各种操作。它们都被放到了java.lang包中,不需要用import java.lang这个语句导入该包就可以直接使用它们。
String类用于比较两个字符串、查找和抽取串中的字符或子串、字符串与其它类型之间的相互转换等。String类对象的内容一旦被初始化就不能再改变。
StringBuffer类用于内容可以改变的字符串,可以将其它各种类型的数据增加、插入到字符串中,也可以转置字符串中原来的内容。一旦通过StringBuffer生成了最终想要的字符串,就应该使用StringBuffer.toString方法将其转换成String类,随后,就可以使用String类的各种方法操纵这个字符串了。
Java为字符串提供了特别的连接操作符(+),可以把其它各种类型的数据转换成字符串,并前后连接成新的字符串。连接操作符(+)的功能是通过StringBuffer类和它的append方法实现的。
例如:
String x = "a" + 4 + "c";
编译时等效于
String x=new StringBuffer().append("a").append(4).append("c").toString();
•