Java 知识学习笔记
1. 基础知识
1. java命令
² javac 编译命令 格式:javac xxx.java 其中xxx代表源程序的名字
² java 解释命令 格式:java xxx
² appletviewer小程序解释命令 格式:javac xxx.java
appletviewer yyy.html
² javap反编译命令 常用格式为:javap [option] class ---javac编译后产生的字节码文件的文件名(注意不带后缀名)
n 参数 –c :反编译指定的字节码文件
n 参数 –l :在反编译的结果中显示行号和局部变量表
n 参数 –public :仅显示public的类和类的成员
n 参数 –protected:仅显示protected/public的类和类的成员
n 参数 –package :仅显示package/protected/public的类和类的成员,这是默认值
n 参数 – private:显示所有的类和类的成员
² FontEnd工具和JAD必须放在一个文件夹中,因为FrontEnd只是JAD的一个界面程序,它需要作为反编译的引擎。(但是对JDK1.4以上不支持L)
² javadoc xxx.java 生成对应的文档
n 注意注释中的前导*允许使用多个,其效果和一个“*”是相同的,但是这几个“*”之间不能用其他的字符分割。如果没有前导“*”,则该行的注释内容从第一个有效字符开始,而不包括前面的空格。
n 注释的内容只能用来说明紧接其后的类,方法或者属性。--放在别的地方无效 将被忽略。
n 常用的标记符号
u @author作者名 ----对类说明:如:@author 张三
@author 李四
显示效果:Author:
张三,李四
如果想将张三和李四显示在不同的行上。@author 张三<br>李四
u @version版本号 ----对类的说明
u @param 参数名 参数说明 ----对方法的说明
u @return 返回值说明 ----对方法的说明
u @exception 异常类名 说明 ----对方法的说明
u @see 类名#方法名或属性名 ---相关主题的参考转向,对类,方法和属性均可说明。如果参考的是同一个类中的方法时,就需要直接用 @see #方法名/属性名
n javadoc –d xxxDoc –version –author –private xxx.java在生成文档时提取出xxx中的版本和作者信息,并且放在一个名字为xxxDoc的目录里面。需要说明的还有,加-private的原因是:javadoc默认的是只提取public类和方法前面的注释,加上后就可以提取外部不可见的类和方法的注释咯。
2. java 基本类型
² 增加了一个byte
3. java断言
² assert +判断的条件
2. Java与面向对象
1. Java的对象导向设计基础、字符串与数组
² Java中有两个用来处理字符串的类:String和StringBuffer,前者用于存储定长的字符串,后者用于存储变成的字符串。
² 判断两个String对象内含的字符串内容是否相同,不是使用“= =”,而是用equals()方法。“= =”比较的是两个对象的地址。
² 在数字向字符串的转换过程中,在其前加一个” ”就可以。这种懒人做法如
n 例子:float a = 788.54F;
String s = “ ” + a; //此时已经float型的数转换成了String类型
² 数组变量的定义和实体配置
n 格式:类型 [ ] 数组变量 = new 类型[元素个数];
n 指定数组元素的初始值。当实体被new出来的时候,里面所有的元素都会被自动的初始化为0(元素类型如果为别的,可能是0,false,null)不过可以用以下语法让它变成我们需要的格式:
数组变量 = new 类型[]{第1个元素值,第2个元素值……}需要注意两点:希望数组中有多少个元素,后面就要给出多少个值;数组内元素的个数由右边的“大括号”决定,所以[]中不能有任何数字。
n 数组的.length,存储数组的长度,在编程时可以直接使用。String对象的字符串内容的长度,用length()方法获得。
n 数组的clone()方法,作用是将该数组的实体复制一份,并且传回所产生的数组实体的参考值,但是所传回的是一个Object都必须以“类型”运算符转换。
u 例 double[] grades = new double[]{89.6,69.7,66};
double[] gradesBK = (double [])grades.clone();//此时已将grades中的内容复制到gradesBK中。
2. 声明类定义及构造其成员
² Java面向对象的重点
n 一般类:
u 封装----public、无
u 修饰符----final、abstract
u 成员变量
l 封装----private、public、protected、无
l 修饰符----static、final、transient…
u 成员函数
l 封装----private、public、protected、无
l 修饰符----static、final、abstract…
l 定义语法
l 参数传递,返回
l Overload(同名异式,重载)
u 构造函数
l 封装----private、public、protected、无
l 定义语法
l 参数传递
l Overload(同名异式,重载)
n 界面类(interface)
u 成员变量----private、public、protected、无
u 成员函数(无执行)
n 继承
u extends----一般类 可Override(覆盖)
u implements 界面类 执行父界面之成员函数
n 对象
u 属性(field)
u 方法(method)
u 事件(event)
² final类拒绝任何类去继承它,所以它不会有任何的子类
² abstract类:它不能产生对象实体,而拥有一个以上的abstract方法的类,就一定要声明成abstract类。
² 如果Java类的成员变量是一个类或者数组的话,在构建该类的实体时并没有对其进行初始化,所以在调用的过程中会出错。---经测验,即使是基本类型也会出错,这个可能是JDK的问题,在eclipse下就可以,汗!
² 在创建一个类的实例对象时,有三件事情:1,为新创建的对象分配内存空间2,初始化对象中实例变量的值3,调用对象的构造函数,在该函数中完成一些初始化工作,也可以什么都不做。
² 尽管在内存中可能创建了一个类的多个对象,但是这些对象中所调用的类的成员函数在内存中只有一个副本。
² Java遍历内存的方式来看,它把内存分为两块,一块是堆内存,一块是栈内存。堆内可以很大,甚至是内存紧张时硬盘都可以,用于存放所有的对象;但是栈内存很有限,通常用来存放全局变量,静态变量和对象的引用。
² 如果即便是在同一个包中,也不希望类被其他类或者子类访问,那么应使用单态模式,核心思想是:采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,且该类只有一个取得其对象实例的方法。
class Singleton
{
private static Singleton _instance = null;
private Singleton()
{
}
public static Singleton instance()
{
if(_instance == null)
{
_instance = new Singleton();
}
return _instance;
}
}
² 成员函数的参数传递,分为传地址和传值。如:类和数组的传递即为传地址。
² 成员函数的重载,在一个类中,允许有多个“函数名”相同,但是“参数列”不同的成员函数,即“同名异式”。
n 成员函数的重载不必考虑“封装等级”,“修饰符”,“返回类型”的差别
² 修饰符final,static,abstract的含义。
n final修饰的成员变量为一个常数,在初始化后不能改变;修饰的成员函数,不允许子类修改
n static修饰成员变量和成员函数都是说明它们为类所有,同一个类的对象,共有同一份“static成员变量/函数”实体,调用时,用类.属性/.方法。
n 成员函数为abstract方法,它只能有方法的“声明部分”,而不能有“实现部分”,且任何有“abstract方法”的类,都必须声明为abstract类。
² 成员的封装等级
n private:对class此类内可见,package,subclass,world不可见
n public: 对class,package,subclass,world均可见
n protected:对class,package,subclass均可见,但是,subclass引用上有所不同
n none:默认的是对class,package和subclass可见。
² 如何声明接口,格式:[封装] interface 接口名称[extends 父接口]
{
public static final 类型 成员变量名 = 值;
public 返回类型 方法名称(参数列);
}
n 接口中的成员变量都会被声明为public static final类型的,即使不加public static final。
n 较少在接口方法的前面加abstract关键字的。默认的为public abstract。
n 接口主要是用来定义一群行为动作的规则,它并不能直接用来产生对象实体,但是也可以定义接口性的对象变量。
² 嵌套类:在类中可以再声明类,内部的类就叫嵌套类,外部的类叫外围类。当设计图形化组件的“事件”(event)时,经常使用到嵌套类。
n 嵌套类有两种,静态嵌套类和非静态嵌套类。静态嵌套类和普通类的用法完全相同,因此很少这样使用,最重要的是非静态嵌套类,也称内部类。
n 内部类通常在类内部定义,也可以在方法体内定义,甚至可以在一个代码块中定义。定义的位置不同,内部类发挥的作用也不同。
n 嵌套类的名称若与“外围类”或者其他类同名,并不会冲突。嵌套类也可能会是一个没有名称的类,即“匿名类”。
n 因为嵌套类算是“外围类”的一个“类型成员”,因此在嵌套类之内,能够存取“外围类”所有的成员,包括private等级的成员,这也是使用嵌套类的重要原因。但是内部类的成员是不能被外部类直接访问的。在使用内部类的成员时,需要先定义一个内部类的对象,通过它来使用内部类的成员,奇怪的是,这里可以通过内部类对象使用类的私有成员,God。内部类的成员是不能声明为static的,否则程序无法编译。
n 可以通过先创建一个外部类的对象,在通过外部类的对象来创建一个内部类的对象来使用内部类。如:
Outerclass outer = new Outerclass();
Outerclass.InnerClass inner = outer.new InnerClass();
inner.xxx;
但这时需要将内部类声明为public的。
n 定义在方法体内的内部类,可以访问外部类的成员,但是只能访问该方法内的final修饰的局部变量,其他的局部变量皆不可以,甚至是static修饰都不可以用于局部变量。
n 内部类可以继承外部类,也可以继承同层次的内部类。
n 匿名类是声明与定义和二为一的类,因此这种类仅使用一次。
3. Java的继承
² extends不仅用在子类与父类之间的继承,父类只有一个,也用在子接口和父接口之间的继承,而父接口可以有N个。
² class 类名 extends 父类
{
public 类名(参数列)
{//如果是没有参数列时,调用父类的默认构造函数,可以不写super();
super(参数值);//参数型式必须是父类构造函数拥有的
…
}
}
n 可以在类的一个构造函数中调用另一个构造函数:使用this(参数列表),但此时就不能使用super(参数列表);
² 子类可以重写从父类继承来的方法。即子类可以定义与父类函数名相同且参数列表相同的成员函数。特别注意的是,子类的返回类型要和原来的相同才行,否则这样将导致编译错误。
² 如果希望继承该类的子类不修改它的某个方法,则可以在这个方法前面加上final限定词。如果某个类不希望被继承也可加final。
² final关键字的合理使用:
n 首先,使用final关键字修饰变量,使该变量在其作用域内不能再被修改。不能被修改,就是变量必须被初始化,之后便不可改变。对基本类型来说,该变量不能再被赋值,对对象变量来说,其引用不能再指向其他对象。
u 初始化的位置有两个:其声明处;在构造函数中为其进行初始化。对于一个变量这两种赋值方式只能采用一种,赋值一次。
u final关键字修饰的变量虽然可以当作常量使用,但是也只能在类内部使用,要想能够在全局有效的常量,必须用public static final共同修饰才可。
n final修饰方法,方法可以被继承,但是不可以被修改。
n final修饰类,类已经很完美,不需要有子类。
² this 和 super两个关键字。当子类覆盖了父类的成员,如果还想使用父类的成员,那么就可以调用super.成员。this.成员使用子类的。
² 在static 方法中无法使用this和super方法。静态变量只能在方法体外的声明才有效。
² static也可以修饰代码块,一般用于对静态成员变量的初始化,只有在类被在载入时才被创建,不依赖于对象实体。程序所有可能用到的类也不是在程序一开始就全被加载,而是当一个程序用到了哪个类,才会去加载那个类。
² static定义的变量会优先于任何其他非static的变量初始化,不论其出现的顺序如何,并且所有用来进行显示的静态变量初始化,只初始化一次,且在类被第一次装载时发生。对于涉及继承关系的时候,一般先初始化父类的static成员,然后子类,依此类推。
² Math.random()方法,生成随机数。
² 如果子类构造函数中没有使用super()调用父类的构造函数,而父类中又没有无参数的构造方法,那么编译肯定出错。因此在定义一个类的时候,如果定义了一个类的带参的构造函数,那么就要定义一个无参的构造函数
² 当一个子类调用super()时,它只会调用离它最近的那个父类的构造函数。
² 对象的多态。使用相同的“对象变量”得到不同的结果,这就是java中的多态现象。这是因为“继承”和“方法覆盖”,导致“父类的对象变量,能够参考子类的对象实体”,而“子类能够改写继承而来的方法”。此处所说的是广义的“继承”,所以包括了extends和implements两种方式。多态更常见于接口。
² 当一个“父类”类型的对象变量参考“子类”对象实体的时候,此时因为“对象变量”是“父类”类型,它就只能使用父类拥有的成员。然而,我们若知道它的“对象实体”确实是某一种“子类”实体,当然就拥有该子类的所有成员,因此,有必要的话,只要将这个父类对象转型为子类对象,就能够使用它的所有成员。(类类型)对象参考
² 抽象类
n 使用与普通类的区别
u 抽象类不能被实例化。
u 抽象类不能定义构造函数,抽象方法不能被声明为静态的
u 抽象方法只需声明无实现。
u 抽象类的子类必须实现抽象类中所有的抽象方法,否则这个子类也是抽象类。
n 可以创建一个不包含abstract方法的abstract类,用于“不必创建abstract的方法,但是又要禁止别人创建这个类的对象”的场合,这是Java允许的。
² 接口中所有成员的访问权限无论是public还是默认的,在实现这个接口的了里中,所有的被实现的方法都必须显式的定义成public的,这样做是保证这些方法的访问权限不会在实现类中受到限制。
² 可以通过仅实现接口中的部分方法来创建一个新的抽象类,但是必须在前面加abstract关键字,当然也可以在已有接口的基础上创建新的接口,用extends,可以继承多个接口。
² 接口中声明的变量一旦定义就不能改变,因此接口可以用作创建常量值的工具。
² 只有非private方法才能够被覆盖。一旦在父类中方法被定义为private的话,只能被隐藏。
4. Java的异常
² Java中所有的异常都继承在java.lang包中的Throwable类。主要分为两类:Error和Exception。Error是较严重的异常。一般情况下,用户自己写的异常至少继承自Throwable类或者子类Exception或者Exception的子类
² 引发异常的方式:一种“由程序系统自动引发”,一种是有程序设计师“使用throw指令引发”。
² 以Java内置的异常类而言,当其中任一种异常状况发生时,都会自动建立一个异常对象,并且将它传给执行时期系统,然后执行时期系统会去寻找负责处理该异常的程序代码(指catch区);也可以视情况自行引发,如使用throw来引发。
² 当某处程序代码引发了一个异常后,若不希望程序的执行因异常而终止,处理方式有两种:一将异常捕捉,二明白指出将方法内产生的异常丢出。
² 捕捉并处理异常try…catch…finally。try块区后面一定要跟一个catch区或者finally区,不可单独使用,catch区可以有多个,finally区视需要添加。
² 若异常类之间有继承关系,“子”异常类应放在“父”异常类的前面
² 指明函数可能丢出的异常---使用throws,格式:
回传类型 函数名(参数列表) throws 异常类1,异常类2,……
{
//……里面的语句可能引发异常
}
如果一个成员函数的“实现部分”内,所写的程序代码有可能会引发异常,而又不想使用try…catch来捕获,可以利用上述语法。那么该成员函数抛出的异常将会在调用它的函数中得到处理。
² 如果一个方法抛出了异常,在使用这个方法的时候,我们应该主动处理这个异常。
² 可以自定义异常类,让它来继承Exception,重写构造函数和getMassage()函数
² 匹配catch子句中参数所给出的异常类型,必须满足以下三个条件之一:
n 异常对象与参数属于相同的异常类;
n 异常对象属于参数异常类的子类;
n 异常对象实现了参数所定义的接口。
² 如果catch子句中没有匹配的异常类型,则异常就会自动被抛出,由Java的虚拟机来处理。
² 在覆盖方法中抛出的异常不能比原方法所抛出的异常类型多,否则即使使用throws语句也有可能无法捕获得到。
² Throwable(Throwable cause) 和Throwable(String message, Throwable cause)其中cause是引起当前异常的异常,即:cause是异常出现的潜在原因。第二种型式允许在制定一个原因异常时再制定一个描述。这样通过一级一级的传递,即便再创建并抛出了新的异常,它仍然能维系一个“能追踪到异常的第一现场的”栈轨迹。这两种构造函数也被追加到Error,Exception和RuntimeException类中,但是其他异常就没有这两个构造函数,所以使用时要谨慎。添加到Throwable中的成链的异常方法是:getCause()和iniCause()。
3. Java GUI界面设计
² 通常我们不能直接使用Frame或JFrame组件,需要声明一个继承自Frame的类,并在里面添加组件作为它的成员。
² 模版如下,使用Frame:
import java.lang.*;
import java.awt.*;
[封装] class 窗口类名 extends Frame
{
n 组件区:Button,Label等类的组件(变量)è类成员
public 窗口类名() //此类构造函数
{
u 决定何种版面编辑方式èFrame的setLayout()方法
u 构造各组件的对象实体è使用组件的构造函数
u 设置各组件的属性:外观位置è各组件setBounds()等方法
u 设置各组件对应的事件监听机制(搭配Inner Class)
u 将各组件加入此Frame中è此Frame的add()方法
u 设置Frame的属性:外观位置等èsetBounds()等方法
u 设置此Frame所对应的事件监听机制(搭配Inner Class)
u 显示此Frameè此Frame中的setVisible()、show()方法
}
public static void main(String[] args)//此方法不一定要有
{
u 窗口类名 窗口对象 = new窗口类的构造函数;//也可在其他类中建立此种窗口对象
u 操作窗口框架è窗口对象.方法
u 操作窗口内的组件è窗口对象.组件.方法
}
n Inner Class(自定义的事件类)è类的类型成员
}
² 模版如下:使用JFrame
import java.lang.*;
import javax.swing.*;
[封装] class 窗口类名 extends JFrame
{
n 组件区:JButton,JLabel等类的组件(变量)è类成员
public 窗口类名() //此类构造函数
{
u 取得JFrame窗口框架内含的ContentPane
u 决定何种版面编辑方式èContentPane的setLayout()方法
u 构造各组件的对象实体è使用组件的构造函数
u 设置各组件的属性:外观位置è各组件setBounds()等方法
u 设置各组件对应的事件监听机制(搭配Inner Class)
u 将各组件加入此ContentPane中è此ContentPane的add()方法
u 设置ContentPane的属性:背景颜色等èsetBounds()等方法
u 设置此JFrame的属性:外观位置等è setBounds()等方法
u 设置此JFrame所对应的事件监听机制(搭配Inner Class)
u 显示此JFrameè此JFrame中的setVisible()、show()方法
}
public static void main(String[] args)//此方法不一定要有
{
u 窗口类名 窗口对象 = new窗口类的构造函数;//也可在其他类中建立此种窗口对象
u 操作窗口框架è窗口对象.方法
u 操作窗口内的组件è窗口对象.组件.方法
}
n Inner Class(自定义的事件类)è类的类型成员
}
² 在编辑的过程中,排版方式:
n 如果没有作出说明,默认的是使用BorderLayout编排版面,即便是一个组件加到容器的中区,也能铺满整个容器。BorderLayout可以有五个方位来存放组件:BorderLayout.NORTH,SOUTH,CENTER,WEST,EAST。如果组件数大于5个将存不下,但是可以通过中间件JPanel。先将组件分组放在JPanel里,再将JPanel放入到Container里面。对每个层次都可以设置布局格式的。
n 如果需要手动设置,则需要在构造函数中加入:this.setLayout(null);
n 如果希望将容器内的组件,组成一个表格状,可以使用GridLayout编排版面,将容器分割成大小相同的个子,让每一个组件填满一个格子。
u this.setLayout(new GridLayout(0, 2));表示的是不限定多少行,但是“列”数为2.,以行为主排列。若指定的行数不是0,则倘若制定的列太多或者太少,默认是会自动分成适当的“列”数,而不是按照指定列数。
n BoxLayout(Container target, int axis)的作用是创建一个新的BoxLayout,欲添加的组件沿着制定的水平或者垂直方法摆放。Target指定了需要防止组件的容器对象,参数axis指定了摆放组件的轴方向,它的取值有:BoxLayout.X_AXIS,BoxLayout.Y_AXIS.
n CardLayout,将要布局的容器看作是一个个的卡片,通过add(容器,“容器名”)方法将容器加入。然后根据条件调用show(CardLayout对象,”容器名”)。
² 在Java中菜单栏,菜单和菜单选项对应的是JMenuBar、JMenu和JMenuItem这三个类来创建。由于JMenu类本身就是JMenuItem类的子类,所以在菜单选项中还可以创建嵌套的子菜单。
n 虽然JMenu类和JButton类具有很多相似的地方,但是JMenu类没有提供可以直接在组件上添加图标的构造函数,但是可以通过setIcon()方法为JMenu组件添加一个图标,从而弥补这个缺陷,使菜单看起来更美观。
n 使用JMenuItem来创建多从层次结构。
n 当按下JMenuItem组件时,如同按下JButton组件一般,均会产生ActionEvent事件,这是最常用的时间。其他的还有:ChangeEvent、ItemEvent、MenuKeyEvent、MenuDragMouseEvent
n JMenuItem中默认的构造函数,如果希望改变突变的位置,则需要SwingConstants这个接口中的静态常量。JMenuItem对象.setHorizontalTextPosition(SwingConstants.xxx);
n JMenuItem 对象.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,InputEvent.CTRL_MASK));//为菜单想增加嫩能够用ctrl控制的快捷键。
n 有时候某些菜单是不可用的,可以用setEnabled()方法禁用或者激活一个菜单喜爱那个。该方法有一个boolean型的参数,当参数为true时激活。
² JTable类按行和列显示数据的组件。要生成一个表格,通常先创建一个JTable对象,同时表格中要显示的数据内容,或者制定显示的模式。然后再创建一个JScrollPane面板,将创建好的JTable对象添加到这个JScrollPane面板中显示。JScrollPane面板容器提供的窗口不能显示整个表格时,可以通过上下关东条和左右滚动条来查看被隐藏的数据内容。
n Table.setPreferredScrollableViedportSize(new Dimension(500,50));//设置可滚动的窗口的大小。
n JTree用来建立树;DefaultMutableTreeNode类建立树节点。
n tree.putClientProperty(“JTree.lineStyle”,”Horizontal/Angled/None”);设置每一个分支节点之间的连线格式。
n 使用DefaultTreeCellRender类来定制视图树的节点。DefaultTreeCellRender cR = (DefaultTreeCellRender)tree.getCellRenderer();然后使用它的方法进行定制。
4. Java的事件监听机制
² GUI组件本身通常不直接处理源自它的事件,而是委任给特定的Listener对象来处理。而这个机制主要包括“事件源”、“监听能力”(Event Listener)、“事件类”:
n 事件源是指引发事件的起源
n 监听能力是监听事件源的能力,即xxxxListener类来实现。
n 传入事件函数的事件都属于xxxEvent类类型的对象,这里的xxxEvent就是事件类。
² 建立GUI事件的做法主要有两种:“实现Listener接口的做法---implements XXXListener”;“继承Adapter类的做法---extends XXXAdapter”两种方式。
XXXAdapter只是implements了XXXListener,但是其中的方法体只是{}所以说,两种方法只是大同小异。
² 按照实际编写程序代码的步骤,一步步设计组件的事件:
n 加入所需的监听能力。方式如:
组件.addXXXListener (给步骤3所产生的XXXListener对象实体)
n 自定义一个实现的XXXListener的类---implements XXXListener
n 产生XXXListener对象实体
² javax.swing包含了几乎所有的Swing组件,且这些组件中的大部分继承自Jcomponent,但是也有一些不是:JFrame,JDialog,JWindow,JApplet。这四个组件必须用到本地代码来显示窗口画面,因而必须使用操作系统的窗口资源。
² JPanel在Java中称为面板,它属于中间容器,是一个轻量级容器组件,透明且没有边框,不能做顶层容器,但可以容纳其他容器,然后再作为整体安置在顶层的容器中。
² JFrame中有层次的概念,一般数值越大,说明该组件位于比其他组件更高的层次,数值越小,说明该组件所在层次越低。
² JFrame有一个特殊的关闭当前窗口的方式setDefaultCloseOperation(xxx.EXIT_ON_CLOSE);//参数有四种型式。
² xxxJPanel.setBorder(new TitiledBorder(“。。。。。”))//为面板显示一个边线,名字为。。。。。
² BorderFactory类。可以设计各种各样的边框。
5. Java多线程机制
² 线程和一个结构化的“程序”非常相似,也是有一个开始点,一串连续的执行过程和一个结束点,而且执行中的每个时间点上也都只有单独一点的执行;但是线程并不是一个“程序”,它不能自己独立执行,得在“程序”之内执行。一个程序可以有多个线程。
² 每个线程都拥有自己的“堆栈”和“程序计数器”,以备用来记录交出控制权时的状态,则每个“线程”都只是在此程序的环境下工作,所以“线程”又被称为“执行环境”。
² 执行Java程序时,执行的进入点就是main()方法,而里面的执行流程,其实就是此程序的“主线程”。对于这个由系统自动建立的“主线程”,我们可以利用Thread类的currentTread()方法来取得它的对象参考,借此可对“主线程”进行一些控制处理。
n 主线程有两个任务:产生其他子线程的任务;通常它必须最后完成执行,这样它就可以执行各种关闭操作。
² Thread.currentThread();//返回的是当前正在执行的Thread。Thread.getName()获得线程的名字。Thread.sleep(毫秒)线程休眠
² 首先声明一个用来产生Thread的类,然后再利用它来建立所需要的Thread对象,声明Thread类的方法有两种选择:继承Thread类和实现Runnable接口。
n 继承Thread类的4个步骤:
1. 该类要继承java.lang的Thread类。
2. 要覆盖由Thread继承而来的run()方法,而run()方法的内容就是此线程要作的任务。当run()执行完,此Thread稍后就会自动终止。
3. 建立此类的对象,亦即产生一个线程对象。
4. 使用此对象的start()方法启动此线程,由JVM自动去调用run()方法。
n 实现Runnable类的4个步骤:
1. 该类要实现java.lang的Thread类
2. 要实现Runnable接口类的方法run(),而run()方法的内容就是此线程要作的任务,当run()执行完,此Thread稍后就会自动终止。
3. 配合此类去建立一个Thread对象实体,亦即产生一个线程对象。但建立Thread对象实体时,还要配合使用Tread类的某些构造函数如:Thread thread = new Thread(new 此类());
4. 使用该Thread对象的start()方法启动此线程,由JVM自动去调用run()方法。
² 显示系统时间的Calendar和GregorianCalendar类。在包java.util.*中;
² 在主程序中创建了一个实现了Runnable接口的类的对象m,然后使用Thread类的Thread(Runnble target, String name)构造函数创建多个线程,最后分别调用start()但是此时的多个线程共享m的数据成员。如果显示问题中要求必须创建多个线程来执行同一个任务,而且这多个线程之间还将共享同一个资源,那么就可以使用实现Runnable接口的方式来创建多线程程序。但通过扩展Thread得不到这样的效果。
² Java中,线程的优先级是介于Thread.MIN_PRIORITY到Thread.MAX_PRIORITY常量之间的某个整数数值(介于1到10之间)。默认线程的优先级为5,在Java中用NORM_PRIORITY来表示。
² 当利用某一个线程又创建了一个新线程,这个新线程默认的优先级跟创建它的线程一样。线程创建以后可以通过使用Thread类的setPriority(int newPriority)方法,它属于Thread类的final方法。可以通过getPriority()来获得线程当前的优先级,该方法也为final方法隶属Thread。如果两个线程优先级相同,则被交替执行。
² 并不是所有系统中运行Java程序都采用时间片策略调度线程,所以一个线程在空闲时应该主动放弃CPU,以使其他同优先级和低优先级的线程得到执行。例如可使用sleep()方法.
² Java虽然支持10个优先级,但是基层操作系统支持的优先级可能要少得多,可能就会造成一些混乱,因此,只能将优先级作为一种很粗略的工具使用,最后的控制可以通过恰当地使用Thread类的yield()方法来完成。
² 在协作模式中,是否能保证线程正常放弃处理器,不掠夺其他线程的执行时间,则完全取决于程序员,可以通过Thread类的yield()方法:使当前的线程从处理器中移出并重新放回到就绪队列中和sleep()来实现:使线程在sleep()中制定的时间间隔内进入睡眠状态,从而放弃CPU。
² 最好不要在同步方法中使用yield()方法,因为它可能会导致当前的线程无法释放锁。否则可以将那些需要同步的代码包在一个同步块中,里面不含有非同步的方法,并且在这些同步代码块之外才调用yield(),问题可解决。
² 线程从创建到消亡存在四种状态:
n 创建状态:new之后
n 可运行状态,对其调用了start()方法后,但并不一定立即运行,这是还需要java的JRE通过合理的调度策略才能使此状态下的线程真正得到CPU。此时线程已被视为活的(alive)。
n 不可运行状态:
u 调用了sleep()方法
u 为等候一个条件变量,线程调用了wait()方法
u 输入输出流中线程被阻塞。
n 消亡状态:一般线程的run()方法执行完毕时,有两种方法使线程进入消亡状态:自然撤销或者强行中止stop()(早期版本)或者interrupt().
² 如何等待一个线程结束:
n 调用线程类的isAlive()方法和sleep()方法来等待某个或某些线程结束。isAlive()方法和循环语句配合使用可以通过判断一个线程是否结束,实现其他线程等待的目的。
n 调用线程的join()方法来等待某个或某些线程结束。它的语法格式为:public final void join()throws InterruptedException,该方法使得当前线程等待调用该方法的线程结束后,再恢复执行。
² 在Java中把线程分为两类:用户线程和守护线程(后台线程)。用户线程就是完成特定任务的一般线程,守护线程是那些仅提供辅助功能的线程。这类线程可以监视其他线程的运行情况,也可以处理一些相对不太紧急的任务。
Thread类提供了setDaemon()方法来打开后者关闭一个线程的守护状态,通过Thread类提供的isDaemon()方法来查看一个线程是不是一个处于守护状态的线程。如果是一个Daemon线程,那么它创建的任何线程也会自动具备Daemon属性。
² 如何中断一个正在运行的线程:这里的中断是指一个线程都在一个任务完成之前被强行停止,提前消亡的过程。
n public void interrupt()。但是它并不能中断,而是继续执行。
n 引入共享变量,是因为该变量可以被多个执行相同任务的线程用来作为是否中断的信号,通知中断线程的执行。为了安全期间,通常需要将共享变量定义为volatile类型或者将对该共享变量的一切访问封装到同步的代码或者同步方法中去。//这是因为使用volatile可以有效的提高效率。
n 如果需要一次中断所有由同一线程类创建的线程,则只需要将共享变量设置为static类型即可。然后在主程序中当需要中断所有同一线程类创建的线程对象时,使用MyThread.stop=true.
n 如果一个线程由于等待某些事件的发生而被阻塞,这时线程根本无法检查循环标志,则需要调用interrupt()方法,因为该方法虽然不能中断一个正在运行的线程,但是它可以使一个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态,推出阻塞代码。但是interrupt()方法只有在线程发生阻塞时才有效。它的作用就是抛出一个InterruptedException类的异常对象,使用try…catch语句捕获异常,并对其进行处理。
² 为了解决线程安全问题,必须引入同步机制。当两个或多个线程需要访问同一资源时,它们需要以某种顺序来确保该资源某一时刻只能被一个线程使用的方式称为同步。实现同步有两种方式:
n 利用同步代码块实现同步:synchronized(Object){//同步代码}。这里的Object可以是比如this,对整个对象都上锁,也是使用虚拟对象来上锁,即只对关键的数据上锁。
n 利用同步方法来实现同步。可以将关键步骤放在一个方法中,在该方法定义的时候加上synchronized关键字。
u 该方法不能被继承。也就是说,在其子类中该方法的重载方法是不会继承其同步特征的。如果需要在子类中实现同步,应该重新使用synchronized关键字来修饰。
u 并不是所有的方法都可以synchronized,比如,run方法。
u 同步方法的使用比同步代码简洁,但是在实际情况中还需要考虑使用那种方式来同步。
² 引起不同步的原因
n 在一个单处理器计算机中,线程的执行由一个处理器来调度,由于处理器的时间片调度原则决定了一个线程仅能执行一定的时间,在其他时间,其他线程也可以执行。
n 在一个单处理器的计算机中,一个线程的执行时间可能没有足够长到其他线程开始执行关键代码前执行完自己的关键代码。
² Java的某些数据类也不能保证原子性的特点,因而也会出现不同步的情况,比如长整型和双精度数。解决的办法就是在这些变量声明前用volatile来修饰。
² 使用同步机制会造成性能的下降,当问题可以确定不需要使用同步机制来解决时,一定不要引入同步方法或者同步代码块。
² 当资源的分配顺序不合理,可能会导致死锁。这时候只能通过ctr+c来结束。使用Decorator模式会较好。
² 线程间的通信问题:wait(),notify(),notifyAll()。它们都是Object类的最终方法,因此每一个类都默认拥有它们。但是只有在synchronized关键字作用的范围内,并且是同一个同步问题中搭配只用这三个方法时才有实际意义。
6. Java的I/O处理方式
² JavaI/O处理方式分为两类:处理纯文本的字符流:基本类为抽象类Reader极其子类,配合使用的有BufferedReader和Writer及其子类,配合使用的BufferedWriter;处理非纯文本的字节流类:基本类为抽象类InputStream及其子类,配合使用的BufferedInputStream和OutputStream及其子类,配合使用的BufferedOutputStream。
² 基本I/O的数据存储类型,主要分为Memery,File和Pipe三大类型。其中Memery一类的流类,就是用来对Memery作存取:File一类的流类则是对作业平台上的文件作存取;而Pipe一类的流类,则是用来实现构成一个Pipe的输入及输出流。
n 不同平台的“名称分隔符”是不一样的,在windows中一般写成“ab//myFile//”,而在UNIX下则要写成“/”,所以为了让程序可以跨平台,一般调用File.separator,让系统根据平台的不同,引入适当的名称分隔符。
² System.in是默认的输入六,而System.out默认的是标准的输出流,默认情况下,它只的是某一个控制台,如显示器。System.err指的是标准的错误流,默认的也是控制台。
7. Applet
² Applet 框架:继承自Java.applet.*;
package 包区;
import package 名称;
public class 主类名 extends Applet(或是JApplet)
{
成员和组件
成员函数
//Applet主要的函数
public void init(){}
public void start(){}
public void stop(){}
public void destroy(){}
}
//一般类区
class 类名 [extends 父类名][implements 接口]
{
成员变量区
成员函数
}
² 上述几个方法的功用:
n init()方法,当Applet程序被载入或重新载入到系统时,浏览器就会自动调用此方法。通常在Applet载入后第一次调用start()之前,会调用init()
n start()方法,每当用户浏览此Applet所在的网页,浏览器就会自动调用此方法,以便开始执行此Applet程序。
n stop(),每当浏览器要离开嵌有此Applet的网页,或者终止浏览器时,浏览器就会自动调用此方法,以便结束此Applet程序的执行。
n destroy(),当此Applet程序已经被利用过,要释放它所占用的资源时,浏览器就会自动调用它,告知此applet程序已被利用过,以进行最后的清除和释放内存的工作,准备卸载此Applet程序。因而调用destroy()之前,通常会调用stop()。
n 除了上述方法以外,其实还有一个public void paint(Graphics g)方法:绘出此组件。当一个组件初次显示,或者组件的外观需要重华时,就会去调用此组件的paint()方法,而Graphics类型的g参数,代表要在此组件范围内绘出的内容。
² 在HTML网页中嵌入Applet程序:
<APPLET
CODEBASE = codebaseURL//如果与使用嵌入它的HTML是在同一目录,在可不指定
CODE = AppletFile//指定欲嵌入的Applet程序类的文件名
NAME = AppletInstanceName//指定此Applet实体的名称
WIDTH = pixels
HEIGHT = pixels
>
</APPLET>