本部分介绍Java实战经典的第三部分:Java应用程序设计
主要包括如下几个重点部分:
重点一:多线程
重点二:Java常用类库
重点三:IO操作
重点四:Java类集框架
重点五:Java图形界面
重点六:数据库编程
重点七:Java新特性:泛型、枚举、Annotation
重点八:Java反射机制
重点九:Java网络程序设计
重点十:Java新IO
第一部分和第二部分重点介绍Java的基础,尤其是面向对象对象的理念。介绍基础语句、基本数据类型、如何构建类、实例化对象、对象的多态性、接口及抽象类的使用
第九章:多线程
本章要点:
了解:进程和线程的区别、线程的状态变化、多线程操作方法、同步和死锁、线程的生命周期
掌握:java多线程的实现方式及区别
进程是代码加载、执行到执行完毕的一个完整过程,这个过程从生命周期上看也是进程产生、发展和消失的过程。由于现代操作系统实行的是分时机制,就是把一段时间分成极短的时间片,然后把时间片分配给各个进程交替执行,但是由于人对极短时间的感应迟钝,所以感觉不到任务是串行的,而是感觉任务是并行的,中间没有停顿。
线程是比进程还要小的单位,运行一个进程可以产生很多个子线程。这些子线程可以同时运行、同时存在。例如打开一个word程序,word进程中可以存在很多个很小的线程,比如字体检查,线程关掉,进程还可以继续存在。
使用多线程能让程序运行效率更高。
JAVA多线程实现
实现多线程有两种手段,一种是继承Thread类,一种是继承Runnable类
方法一:继承Thread类
Thread类在java.lang中定义
多线程定义语法:
class 类名称 extends Thread{
属性;
方法;
public void run(){//覆写Thread类中的run方法,此方法是线程主体
线程主体
}
};
调用时候调用Thread类的start()方法
通过此方法调用时,调用时只能调用一次start()方法,大于一次会报IllegalThreadStatesException
一般调用是通过Runnable来实现多线程的多次调用
Runnable是一个接口,只有一个抽象函数publi void run(),需要实现。
使用方式:
Class 类名称 implements Runnable
{
属性;
抽象方法;
public void run(){
线程主体//覆写Runnable的方法
}
}
启动多线程方式还是依靠Thread类,在Thread类中有两个构造方法
public Thread(Runnable target)和public Thread(Runnable target,String name)
通过先实例化Thread类,再将Thread类向上转型就可以Runnable实例调用start()方法
区别与联系:
Thread类实际上是Runnable类的实现。
由定义可以看到
class Thread extends Object implements Runnable
继承Thread类不能实现资源共享,使用Runnable类好
有如下两段代码,都是展示Runnable实现多线程的
class ThreadDemo004 implements Runnable{
public void run(){
for( int i = 5 ;i < 100&i > 0;i--){
System.out.println("余票:" + i);
}
}
}; public class ThreadDemo003 {
public static void main(String args[]){
ThreadDemo004 td1 = new ThreadDemo004();
new Thread(td1).start();
new Thread(td1).start();
}
};
上述程序运行结果是:
余票:5
余票:4
余票:3
余票:2
余票:1
余票:5
余票:4
余票:3
余票:2
余票:1
也就是没有实现多线程的资源共享,这个例子中,第一个例子开启了一个进程i是局部变量,属于第一个进程的,不可能共享,需要用下面的程序实现。
另一个程序是:
class ThreadDemo004 implements Runnable{
private int ticket = 5;
public void run(){
for( int i = 5 ;i < 100;i++){
if(ticket > 0)
System.out.println("余票:" + ticket--);
}
}
};public class ThreadDemo003 {
public static void main(String args[]){
ThreadDemo004 td1 = new ThreadDemo004();
new Thread(td1).start();
new Thread(td1).start();
}
};
实现Runnable接口相对于Thread类有如下优势:
1、 适合多个相同程序代码的线程处理同一资源的情况
2、 避免java单继承带来的缺陷
3、 增强代码健壮性,代码可以被多个线程共享,使代码与数据独立
总之:建议使用Runnable
线程的状态:
要想实现多线程,必须在主线程(主方法)中创建多个线程对象,线程一般具有5种状态:
创建、就绪、运行、阻塞、终止
1、 创建状态
在程序中用构造方法创建一个线程对象后,新的线程就处于创建状态,此时有相应的内存空间和其他资源,但处于不可运行状态。新建一个线程可以使用Thread类进行。如Thread a = new Thread
2、 就绪状态
创建完后,使用start()方法启动,启动完线程后,程序就进入就绪状态,线程进入线程队列排队,等待CPU服务
3、 运行状态
当就绪状态的线程获得CPU资源后,进入运行状态,自动调用该线程的run()方法,run()方法定义了改方法的的操作和功能
4、 堵塞状态
正在执行的线程遇到突发情况,被人为挂起,或遇到耗时的输入输出操作,将让出CPU,并暂时中止执行,进入堵塞状态,也可以叫暂停状态。在可执行的状态下,调用sleep(),suspend(),wait()方法,都可以进入堵塞状态。堵塞状态时,线程不能进入线程队列排队,只有解除堵塞状态后,才可以转入就绪状态。
5、 死亡状态
程序执行完毕,或调用stop()方法后,线程将死亡,处于死亡状态的线程,不具有继续执行的能力。线程死亡也意味着其占用的资源被回收
线程的操作方法
线程的主要操作方法在Thread类中,并不在Runnable中
介绍常用的线程方法:
1、 取得和设置线程名称
getName()方法获取名称,setName()设置名称。线程名称一般在启动时候设置,但允许运行时候进行设置。允许两个Thread对象有相同名称,但尽量避免发生。如果没有设置名称,系统会自动为线程分配名称。主方法也是一个线程。
JAVA程序每次运行时候,至少启动两个线程,一个是main线程,一个是垃圾回收线程
2、 判断线程是否启动
使用isAlive方法,通过Thread类的start()方法通知CPU这个线程已经准备好启动,然后等待CPU分配资源。运行此线程,java通过isAlive()方法来测试进程是否已经在启动而且仍然在启动。
由于线程操作的不确定性,主线程可能比其他进程先执行完。。。奇怪。。。
3、线程的强制运行
使用join()方法能够使一个线程强制运行。线程强制运行期间,其他线程禁止运行,直到此线程运行结束。
4、线程的休眠
使用Thread.sleep()进行休眠,sleep()方法中间的参数是时间,单位是毫秒。Ms
5、中断线程
使用方法interrupt()方法中断线程
6、设置后台进程
使用方法setDaemon()方法
7、线程的优先级
线程操作时,所有的线程就已经处于就绪状态,那么哪个线程先运行是个问题。解决方法是使用优先级策略,优先级高的优先可能被处理。
使用方法setPriority()
Max :10
Norm :5
Min:1
实际使用时候,并非设置优先级高就一定先执行,具体由CPU调度执行
主方法的优先级
使用方法getPriority
8、线程的礼让
使用方法yield()
同步与死锁
使用Runnable进行多线程处理的特点是资源被多个线程共享。这样可能导致一个问题是多个线程操作同一个资源的同步问题。比如卖票为负数的问题。
解决方法是使用同步方法:思想是,同一个资源被某个线程占用时候,其他线程禁止访问,直到操作结束。具体操作方法上有两种:1
1、 使用同步代码块。2、使用同步方法
方法一:使用同步代码块
使用关键字synchronized(同步对象){
需要同步的代码块
}
使用synchronized时候,需要设置同步对象,一般使用this,设置当前操作对象为同步对象,就是this表示后面的大括号部分,作为一个整体,设置为同步对象。
方法二:使用同步方法
同步方法是将代码块包起来变成一个方法而已。语法如下:
Synchronized 方法返回值 方法名称(参数列表){
}
Synchronized是本书的最后一个关于方法的关键字。
访问权限{public,default,protected,private}【final,static,synchronized】
【返回值类型,void】 方法名称(参数类型 参数名称)【throws Exception1,Exception2】{
Return 返回值|返回调用
}
死锁:
死锁是两个线程都以对方完成为条件才能继续下去,也就是都等待对方完成。
死锁是资源同步过多造成的。
泛型
泛型从字面理解是泛泛的数据类型,就是数据类型是不固定的。什么时候不固定呢?定义变量时候可以定义一个返回类型不固定的类型,具体使用时候,再根据实际情况返回所需要的类型。
泛型可以解决安全性问题。
使用语法:
在类声明时候通过一个泛类型标识表示类的某个属性的类型,或者某个方法的返回值及参数类型。这样在类实例化或声明时候,指定好类型即可。
泛型类的定义:
访问权限 class 类名称 <泛型类型标识1,泛型类型标识2,泛类型标识3>
访问权限 泛类型标识 变量名称;
访问权限 泛类型标识 方法名称(){}
访问权限 泛类型标识 返回值类型 方法名称(泛类型标识 变量名称){
}
例如
class JiaWei<泛型类型>{
修饰符 泛型类型 属性名称
public 泛型类型 setter(){}
public void 泛型类型 getter(泛型类型 变量)
}
泛型对象定义
类名称<具体类> 对象名 = new 类名称<具体类>()
class Point < T >{
private T var;
public T getVar(){
return var;
}
public void setVar(T var){
this.var = var;
}
}
<T>表示泛型,类型T是由外部调用本类时候指定的,可以使用任意字母表示<A>、<B>等等,T是type的首字母大写,这样比较好理解。
加入泛型的最大好处就是避免了类的转换异常
泛型应用中的构造方法
构造方法可以为类中的属性初始化,如果类中的属性类型通过泛型指定,而又需要通过属性设置构造内容时,构造方法和之前并没有异同,不需要像声明类那样指定泛型
访问权限 构造方法(泛型类型 参数名称)
指定类的多个泛型
类中涉及到的属性可能不止一个类型,可以在声明类的时候加上多个泛型。
class TangBao<K,V>
在实例化泛型时候最好同时写出实例化对象的泛型类型,否则会有安全警告
比如有个类class TangBao<K,V>
在主类实例化时候:可以这样写Tangbao jiawei = new Tangbao();
这样的写法是不安全的。要指明类型带上泛型。
TangBao<String,Integer>jiawei = new TanBao<String,Integer>();
也可以不具体指明是哪种,指出是Object统一接收。这样的好处是虽然没有写明是哪个,但是可以消除警告信息。
通配符:
在泛型类型中,可以设置通配符来接收任意类型的对象。通配符是:?
泛型传递后面跟上?表示接收任意类型对象
泛型的范围:
主要从继承 的角度来限定对象传递的范围
使用两个关键字:一个是super,一个是extends
Super是规定对象传递下限的,extends规定上限的
定义上限:
类的定义:访问权限 类名称 <泛型标识 extends 类名称>
对象的定义:类名称<? Extends 类名称> 对象名
设置下限
类定义:访问权限 类名称<泛型标识 extends 类>{}
对象声明:类名称<泛型标识 ? super 类> 对象名
泛型与子类继承的限制
一个子类可以通过对象的多态性为其父类实例化,在泛型中,子类的泛型类型,无法使用父类的泛型来接收。如Info<String>不能用Info<Object>
泛型接口
也可以定义泛型接口
访问权限 interface 接口名称<泛型标识>.
本章是将泛型重新应用于类、对象、方法、接口的定义上,使
相应的处理能够一般化
Annotation注释
本章是讲在程序中嵌入注释的,与之前的//形式注释不同,这种注释可以明确在程序中展现,并且可以在出错时候打印出来,在控制台展现。
@Override用于覆写父类方法
@Deprecated用于声明一个不建议使用的方法
使用后不建议使用的方法名称上会被打上横线
@Deprecated也可以用于类上面
@SuppressWarnings压制警告,例如声明一个类时候,没有声明泛型,就会出现警告,使用该注释可以压制警告
使用语法:
@SuppressWarnings({“unchecked”,“deprecation”})
被压制的警告有如下:
deprecation 使用了不赞成使用的类或方法时的警告
unchecked 执行了未检查的转换时警告,例如泛型操作中没有指定泛型类型
fallthrough switch操作时case后未加入break操作,导致程序继续执行其他case语句时出现的警告
path 设置了一个错误的类路径、源文件
serial 当在可序列化的类上缺少serialVersionUID定义时的警告
finally 任何finally子句不能正常完成时的警告
all 以上所有警告
反射机制
反射机制是正常机制的逆行
例如声明了一个类Person后,可以通过对象反向找出实例化对应的类名
比如per是Person类的一个对象,调用方法getClass(),per.getClass()将反向获得类,再使用getName()方法获取类名
per.getClass().getName()
getClass是Object类的方法
正常方式:引入需要的“包.类”名称→通过构造方法实例化→取得实例化对象
反射模式(逆向模式):实例化对象→getClass()方法找到对应的类→取得完整的“包.类”名称
Object类是一切类的父类,所有类的对象是Class类的实例。Class也是一种类,是java.lang.Class
删除指定范围内的字符串
使用方法delete();
indexOf查找指定字符串内容,如果未查到返回-1.
Runtime类 运行时操作类,一个封装JVM进程的类,可以进行JVM级别的底层操作。每一个JVM都对应一个Runtime类的实例。获取JVM实例的方法是:
Runtime run = Runtime.getRuntime();
介绍Runtime类常用的方法:
内存信息类:
maxMemory 最大内存空间
freeMemory 空闲内存空间
没有最小内存量
Runtime类与Process类
可以Runtime类运行本机可执行程序
可以用exec()方法,返回的是Process,可以用Process类来控制进程。
使用destroy()方法
国际化程序
使程序适用于多个国家语言。使用Locale类、属性文件及ResourceBundle支持
属性文件是后缀名为.properties的文件,文件内容结构为key = value
如:
WizardFrame.install = 安装
WizardFrame.uninstall = 卸载
WizardFrame.Verify = 校验
WizardFrame.next = 下一步
WizardFrame.nextCMD = 下一步>
WizardFrame.previous = < 上一步
WizardFrame.previousCMD = 上一步
WizardFrame.cancel = 取消
WizardFrame.hint = 提示
WizardFrame.confirmation = 确认
WizardFrame.InstallAffirmCancel = 确定取消安装吗?
WizardFrame.UninstallAffirmCancel = 确定取消卸载吗?
WizardFrame.VerifyAffirmCancel = 确定取消校验吗?
WizardFrame.UpdateAffirmCancel = 确定取消升级吗?
ResourceBundle类
getBundle(资源名),getBundle(资源名,区域码)
动态文本:
动态文本也可以叫可变文本,就是文本的部分内容不是写死的,需要用户的交互输入。
比如说需要输出:你好,XXX!
XXX就需要用占位符来表述{0},如果需要多个占位符,就用{1},{2}表示。可以使用MessageFormat类来处理占位符内容。起使用的方法是:
format
format(String pattern,Object···arguments),其中Object···arguments表示任意多个参数,没有个数限制
这个是java的新特性
返回值类型 方法名称 (Object ···arguments),表示方法可以接受任意多个参数,然后按照数组方式输出
除了使用资源文件保存属性信息外,还可以使用类来保存属性信息。不过这样比较少见
System类
System类是与系统相关的一些属性和方法集合,这些属性和方法都是静态的,都是使用static来定义。
getProperty可以用来获取系统的一系列属性
垃圾对象的回收:
Runtime类有个垃圾回收方法是gc(),System类也有一个gc()方法用于回收垃圾,这个是对Runtime垃圾回收的封装事实上。
一个对象如果不被任何一个栈内存所引用,这个就成为垃圾,需要等待被回收,等待时间不确定。可以直接调用System.gc()方法来进行回收
使用gc()方法时,会默认调用一个finalize()方法,进行一些处理,所以System的gc方法是一些方法的封装,
对象的生命周期
类的初始化——对象的实例化——对象的垃圾收集——对象的终结——程序卸载对象
一个类加载后进行初始化,然后可以对对象实例化,对象实例化由构造方法来完成,当一个对象不再使用时等待被垃圾回收,然后对象终结,最终被程序卸载
日期操作类
Date类
使用date()方法获取一个完整的日期
Calendar类
这是一个抽象类,需要利用多态性,转成父类,再调用子类覆写的方法
用子类的构造方法在栈内存中实例化
DateFormat类
DateFormat类和MessageFormat类都属于Format类,用于专门格式化数据。也是一个抽象类,需要子类实例化