一、文档注释
1.语法格式:、/** */
2.作用:
用javadoc.exe工具,生成API帮助文档
3.如何使用javadoc.exe生成API
tools菜单–>generate JavaDoc…–>
/**
* 这是程序的入口main方法
* @param args String[] 命令行参数,在运行时可以传入参数值,格式:java 主类名 参数值1 参数值2 。。。
*/
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/**
* 文档注释的演示
*
*
* @author Irene
* @since 1.0
* @see java.lang.Object
*/
public class TestJavadoc {
/**
* 这是程序的入口main方法
* @param args String[] 命令行参数,在运行时可以传入参数值,格式:java 主类名 参数值1 参数值2 。。。
*/
public static void main(String[] args) {
}
/**
* 求两个整数的和
* @param a int 加数1
* @param b int 加数2
* @return int 返回a+b
*/
public static int sum(int a, int b){
return a + b;
}
/**
* 复制一个文件
* @param srcFileName String 源文件路径名
* @param destFileName String 目标文件路径名
* @throws FileNotFoundException 当源文件不存在,会抛该异常
*/
public static void copyFile(String srcFileName,String destFileName) throws FileNotFoundException {
FileInputStream fis = new FileInputStream(srcFileName);
//...
}
/**
* @param
*/
public void test(){
}
}
二、异常
1、什么是异常?
以下情况不是异常?
(1)语法错误,编译不通过
(2)逻辑错误,正确的输入得不到正确的输出
什么是异常?
正常情况下是可以运行的,程序也没有逻辑错误,但是因为一些因素:
(1)用户的错误输入
(2)网络中断
(3)用户的余额不足
(4)用户的磁盘空间不够
…
导致程序无法正常运行,甚至发生异常或错误,程序崩溃。
2.异常的额概述
在Java中一切皆对象,包括异常,也是用对象表示的。
异常的根类型是java.lang.Throwable。
API:
Throwable 类是 Java 语言中所有错误或异常的超类。
(1)只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。
(2)只有此类或其子类之一才可以是 catch 子句中的参数类型。
Throwable 类在Java中有两大子类:
Error:严重的错误
指一般的合理的应用程序不应该视图捕获的严重错误,例如 :VirtualMachineError(虚拟机错误),
包括StackOverflowError(栈内存溢出)、OutOfMemoryError(堆内存溢出OOM)
如果发生类似的错误,必须停下来,然后修改代码或者是升级硬件。
Exception:一般的异常
指出了合理的应用程序想要捕获的条件。
3.异常的处理 try cath
(1)注意:无论是JVM抛出的异常,还是我们程序员手动抛出的异常,都可以使用try…catch处理。
(2)如何处理,语法格式
try{
可能发生异常的代码
}catch(异常类型1 异常对象名称){//习惯上异常对象名称都叫e
尝试处理异常的代码,
或者是打印异常信息的代码(打印可以是在控制台打印,也可以是输出到日志文件)
}catch(异常类型2 异常对象名称){//习惯上异常对象名称都叫e
尝试处理异常的代码,
或者是打印异常信息的代码(打印可以是在控制台打印,也可以是输出到日志文件)
}。。。。
finally{
最终一定会执行的代码,无论try中是否发生异常,也不管catch是否可以捕获异常,
也不管try…catch中是否有return语句,都一定会执行finally中的代码。
只有一种情况不会执行,在try或catch中有System.exit(0)并执行了,退出JVM。
}
(3)如何运行
情况一:try中没有异常,不需要判断catch,不用进行捕获
情况二:try中发生异常,
首先,try中发生异常的语句后面的代码就不执行了
第二,此句代码就抛出一个异常对象,然后catch就开始尝试捕获,按照顺序判断,是否能够匹配该类型
如果有一个catch分支可以捕获异常,那么说明程序不会挂断,从try…catch下面继续运行。
如果没有一个catch分支可以捕获异常,那么当前方法就会挂掉,把异常抛给调用者。如果已经是main了,那么程序就挂了。
public class TestTryCatch {
public static void main(String[] args) {
//选中要try的代码,然后按ctrl + alt + T
try {
//从命令行接收2个整数,求他们的商
//(1)命令行接收的数据,是String类型,所以要先转为int
int a = Integer.parseInt(args[0]);
int b = Integer.parseInt(args[1]);
int result = a/b;
System.out.println("商:" + result);
} catch (NumberFormatException e) {
e.printStackTrace();//打印异常的堆栈跟踪信息
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
System.out.println("后面其他的代码");
}
}
4.throws关键字
情形一:在try中发生异常,有catch捕获异常,程序就正常运行
情形二:当前方法中不适合处理异常,要throws抛给调用者处理,如果直接调用者可以处理,那么在直接调用者处就处理了,
如果不行,接着throws,但是最后在main中如何还不处理,程序会挂了。
2.语法格式:
【修饰符】 返回值类型 方法名(【形参列表】) throws 异常列表{
}
3.异常列表:可以是1个或多个,多个之间使用,分割。、
说明:(1)一般来说,throws抛出的都是编译时异常,很少抛出运行时异常。
因为运行时异常的话,是希望我们程序员尽量避免,通过各种if条件判断,避免运行时异常,或者小心一点。
例如:数组下标越界ArrayIndexOutOfBoundsException,应该可以避免
空指针异常NullPointerException,在用对象调用方法等之前,应该加if(xx != null)判断
类型转换异常ClassCastException,在向下转型之前,应该加if(xx instanceof 类型)判断
(2)如果throws的是运行时异常,调用者如果不看API或源码,我们可能都不知道该方法throws了异常,效果不明显。
但是写了呢,ctrl +alt + T,选择try...catch时,可以更明确的catch对应的异常。
(3)方法重写时,对throws的异常有要求
父类类中被重写的方法throws的异常 >= 子类中重写的方法throws的异常
反过来说,
子类重写时,throws的异常 <= 父类类中被重写的方法throws的异常
方法重写的要求: 子类重写 与 父类被重写比较
(1)方法名:必须相同
(2)形参列表:必须相同
(3)返回值类型:
基本数据类型和void:必须相同
引用数据类型:<=
(4)权限修饰符:>=
(5)不能是这些修饰符:static,final,private等
(6)throws异常类型:<=
public class TestThrows {
public static void main(String[] args) {
try {
copyFile("d:/1.txt", "d:/atguigu/1.txt");
} catch (FileNotFoundException e) {
System.out.println("源文件不存在,请检查...");
}
System.out.println("其他的代码");
try {
method();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void copyFile(String srcFileName, String destFileName) throws FileNotFoundException {
FileInputStream fis = new FileInputStream(srcFileName);
//...
}
public static void method(){
throw new NullPointerException();
}
}
class Father{
public void method()throws NullPointerException{
///...
}
}
class Sub extends Father{
@Override
public void method() throws NullPointerException {
//
}
}
5、throw关键字
作用:手动抛出异常
Java的异常对象有两种情况会抛出:
(1)JVM检测到异常,并且自动抛出
(2)我们在代码中,自己用throw语句抛出
我们手动抛出异常的目的是:(1)提示调用者xx不合适(2)改变程序的执行流程
语法格式:
throw 异常对象;
throw new 异常构造器([…]);
public class TestThrow {
public static void main(String[] args) {
Account account = new Account("11111",1000);
Scanner input = new Scanner(System.in);
while(true) {
try {
System.out.print("请输入金额:");
double money = input.nextDouble();
account.withdraw(money);
break;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class Account{
private String id;
private double balance;
public Account() {
}
public Account(String id, double balance) {
this.id = id;
this.balance = balance;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Account{" +
"id='" + id + '\'' +
", balance=" + balance +
'}';
}
public void withdraw(double money){
if(money < 0){
throw new IllegalArgumentException(money + "不能为负数");
}
if(money > balance){
throw new IllegalArgumentException(money + "大于余额");
}
balance -= money;
}
}
6.自定义异常
(1)为什么要自定义异常类型?
一个异常:
①类型名
②message信息
③堆栈跟踪信息
④…
其中类型名是最最关键字的,我们看到这个类型名,就应该知道它发生什么状况了,该从哪个方向去解决。
例如:ArrayIndexOutOfBoundsException
NullPointerException
FileNotFoundException
当我们的项目中,有些情况要抛出异常告诉调用者发生xx事情了,但是现有的系统中的异常类型不能准确的表达我的情况,就需要自定义异常。
(2)如何自定义异常?
第一步:必须继承Throwable或它的子类,一般是继承Exception或,RuntimeException
第二步:建议,我们保留一个无参构造,然后再加一个有参构造,为从父类继承的message赋值
第三步:建议,实现序列化接口(后面再详细讲解序列化接口)
(3)如何使用自定义异常?
要求:只能手动throw
public class TestDefineException {
public static void main(String[] args) {
Account account = new Account("11111",1000);
Scanner input = new Scanner(System.in);
while(true) {
try {
System.out.print("请输入金额:");
double money = input.nextDouble();
account.withdraw(money);
break;
} catch (MoneyCanNotNativeValueException e) {
e.printStackTrace();
} catch (BalanceNotEnoughException e) {
e.printStackTrace();
}
}
}
}
class MoneyCanNotNativeValueException extends Exception{
public MoneyCanNotNativeValueException() {
}
public MoneyCanNotNativeValueException(String message) {
super(message);
}
}
class BalanceNotEnoughException extends Exception{
public BalanceNotEnoughException() {
}
public BalanceNotEnoughException(String message) {
super(message);
}
}
class Account{
private String id;
private double balance;
public Account() {
}
public Account(String id, double balance) {
this.id = id;
this.balance = balance;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Account{" +
"id='" + id + '\'' +
", balance=" + balance +
'}';
}
public void withdraw(double money) throws MoneyCanNotNativeValueException, BalanceNotEnoughException {
if(money < 0){
throw new MoneyCanNotNativeValueException(money + "不能为负数");
}
if(money > balance){
throw new BalanceNotEnoughException(money + "太大,余额不足");
}
balance -= money;
}
}
三、多线程
1、相关名词(了解)
程序:program 它是为了完成一个功能,或者一个目的,选择其中一种编程语言(C,Java,Python等)而编写的一组指令的集合。
它是静态的。
进程:process 它是程序的一次运行。
操作系统会以进程为单位,分配资源(内存等)。每一个进程之间是独立的。
如果两个进程之间要进行数据的通信,那是成本很高的,因为他们之间没有共享的内存位置。
只能通过网络通信,或者是通过外置的硬盘等空间来实现数据的共享。
而且CPU在进程之间的切换的成本也很高。
线程:thread 它是进程中的其中一条执行路径。
即一个进程至少有一个线程(称为单线程程序),也可能是多个线程(称为多线程程序),
同一个进程的多个线程之间是可以有共享的内存,那么线程之间的通信成本就降低了。
而且CPU在同一个进程的多个线程之间切换的成本相对较低。
CPU调度的最小单位是线程。
并发:
早期的电脑是只有一个CPU,并且是单核。
同一时刻,一个CPU只能运行一个线程的指令。但是我们编写的程序有多个线程。
如果说大家按顺序执行,那么就说明是单线程的现象,如果前面有句代码阻塞(例如,等着用户输入,等着从硬盘加载数据)了,后面的代码只能干等着,
这个时候CPU的利用率很低。
为了提高CPU的利用率,那么CPU就先将该线程“暂停”,然后转而去执行其他的线程。
这个时候从宏观角度(人的感觉),好像是多个线程同时执行,但是本质上是轮流执行,或者说是抢夺执行,即从微观(CPU)来说,
是先执行一个线程,再执行一个线程,他们之间是快速的切换,我们都感觉不出来。
并行:
现在的电脑是多个CPU,是多核。
这个时候我们可以把多个线程分别派发给多个CPU分别执行,这个叫做并行处理。
现在的程序是并行+并发。
2、Java程序的线程是什么样的呢?
(1)Java程序生来就是多线程的。
第一:有一个main线程(主线程)
第二:GC线程
第三:异常检测和处理线程
(2)那么我们除了main线程、GC等以外,能否开启其他的线程呢?
可以
四种方式:
方式一:继承Thread类
方式二:实现Runnable接口
方式三:实现Callable接口
方式四:线程池
我们JavaSE阶段只学方式一和方式二。
3、继承Thread类实现多线程
步骤:
(1)定义自己的类,继承Thread类
(2)重写public void run()方法
如何重写?看你这个线程要干什么?
(3)创建线程对象,并且启动这个线程
必须调用start()方法
特别说明:不要手动调用run()
public class TestExtendsThread {
public static void main(String[] args) {
MyThread my = new MyThread();
my.start();//从父类Thread继承的 一旦该线程被启动了,就准备好和main线程强CPU了,“同时”运行
// my.run();//不要手动调用run(),否则就不是多线程了
//在main线程中打印1-100之间的奇数
for (int i=1; i<=100; i+=2){
System.out.println("main:" + i);
}
}
}
class MyThread extends Thread{
//例如:我要在这个线程中打印1-100之间的偶数
@Override
public void run(){
for (int i=2; i<=100; i+=2){
System.out.println(i);
}
}
}
4、实现Runnable接口
步骤:
(1)编写自己的线程类,实现java.lang.Runnable接口
(2)重写public void run()方法
(3)创建自定义线程对象
(4)创建Thread类的对象,并且把刚刚我们创建的自定义线程对象传给Thread对象
(5)调用Thread类对象的start方法
为什么要调用start方法呢?
是为了调用start0方法,即C语言实现的方法,它是操作系统底层的函数库中的方法,
相当于我们的多线程程序要交给操作系统来管理。
public class TestImplRunnable {
public static void main(String[] args) {
MyRunnable my = new MyRunnable();
// my.start();//没有这个方法
Thread thread = new Thread(my);
thread.start();
//在main线程中打印1-100之间的奇数
for (int i=1; i<=100; i+=2){
System.out.println("main:" + i);
}
}
}
class MyRunnable implements Runnable{
//例如:我要在这个线程中打印1-100之间的偶数
@Override
public void run(){
for (int i=2; i<=100; i+=2){
System.out.println(i);
}
}
}
5、使用匿名内部类的方式来继承Thread或实现Runnable
public class TestAnoymous {
public static void main(String[] args) {
//一个线程打印奇数
new Thread(){
public void run(){
for(int i=1; i<=100; i+=2){
System.out.println("奇数:" + i);
}
}
}.start();
//一个线程打印偶数
/* Runnable r = new Runnable(){
public void run(){
for(int i=2; i<=100; i+=2){
System.out.println(i);
}
}
};
Thread t = new Thread(r);
t.start();*/
new Thread(new Runnable(){
public void run(){
for(int i=2; i<=100; i+=2){
System.out.println(i);
}
}
}).start();
}
}