本篇文章是《零基础学Java》专栏的第七篇文章,文章采用通俗易懂的文字、图示及代码实战,从零基础开始带大家走上高薪之路!
本文章首发于公众号【编程攻略】
包
包是java中对类的组织管理形式。java中的所有的资源也是以文件方式组织,这其中主要包含大量的类文件需要组织管理。java中同样采用了目录树形结构。虽然各种常见操作系统平台对文件的管理都是以目录树的形式的组织,但是它们对目录的分隔表达方式不同,为了区别于各种平台,java中采用了"."来分隔目录。
包机制也解决了类名冲突问题,一个包就是一组相关的类和接口,同一包中不能有同名的类或接口。
包同时也实现对类及其成员的保护。一个.java
文件通常称为一个compilation unit
。每个源文件中可以有若干个类,但最多只有一个类前可有 public
修饰,该类的名字和文件名同名,这种类在包外是可见的,能被调用。其它非public的类在包外不可见,这些类为public 类的支持类。
当编译一个 .java
文件时,该文件中每一个类都会生成对应的,类名和文件名同名的,后缀为.class
的文件。
在文件系统的存储上,一个包对应一个文件夹,包中还可以有子包,子包对应子文件夹,包中的类对应文件系统中的.class
文件。
classpath
Java中的资源存在于不同平台下时,它们的存放方式及位置必然会有很大差异。因此跨平台的java包结构和平台之间必须通过一种方式来衔接到一起。这个衔接方式是通过classpath
实现。classpath
针对java类的存放位置,告诉java虚拟机(jvm)要使用或执行的*.class
文件放在什么地方。如同windows
中的path
的作用。
在jdk6中,系统类的位置不需要设置classpath
,但是如果需要指定用户定义类的位置,则需要设置classpath
了,同时用户设置的位置不会影响系统查找系统类。
比如在c
盘建立一个目录java
,将某个类,比如test.class
放在该目录下,然后转到其它目录下,再执行java test
,会发现找不到该类,这时可以将classpath
设置为c:\java;
,再执行java test
,会发现可以运行了,如果在当前目录下再放一个和test
同名但功能不同的类文件,再执行java test
,看看到底执行的哪个test
呢?
包的声明与编译
声明包的格式如下:
package 包名;
该语句必须放在java源文件中的第一行,且最多只能有一条。子包和父包之间通过.
连接。该语句指定本源程序编译生成的所有类的存放位置,既是所处的包。
编译方式:
带有package语句的java源文件必须这样编译:
javac -d 生成路径 java源文件存放位置路径
该命令会自动在“生成路径”所指定的位置,生成package所指定包层次对应的文件夹结构。
如下程序示例:
package edu.henu.rjxy.wht;
public class test{
public static void main(String[] args) {
System.out.println("中 国");
}
}
import edu.henu.rjxy.wht.test;
public class t1{
public static void main(String[] args) {
test.main(args);
//edu.henu.rjxy.wht.test.main(args);
}
}
import的使用
加包名使用类很不方便,于是引入import语句,它的格式如下:
import 包名.类名;
import 包名.*;
import 告诉编译器引入一个包(package),包是类的集合。
当使用标准Java库中的组件时,可以这样:
import java.util.Scanner;
这告诉编译器要使用Java的 Scanner
可是 util 中有很多类,要使用它们没必要一一列举出来,可以使用通配符:
import java.util.*;
另外,java.lang包中的类不需使用import来引入。
import static
用于引入某个类的静态成员,缺点在于如引入的多个类中有同名的静态成员就会出错,如下:
import static java.lang.Math.*;
异常
异常指程序运行时出现的非正常现象,对异常的处理通常有两种方法:
- 系统检测到程序的错误,给出错误信息并终止程序的运行
- 在程序中加入处理异常的功能
异常类的继承简略图如下:
类 Throwable
是 Java 语言中所有错误或异常的父类。
在Java中,将异常情况分为Exception
和Error
两大类,因此有两种Throwable
对象
Exception
解决程序及环境产生的异常,可由程序捕获并处理Error
处理较少发生的内部系统错误,不能被程序捕获,只能由用户按照系统提示关闭程序。
异常类的名字就代表了可能发生的异常的含义。该异常类的对象代表真正所发生的异常,但注意它不是异常本身。
异常并不都是在 java.lang
包中定义,其它如util
, net
, 及io
中均有定义。所有的 I/O 异常都继承自java.io.IOException
。
那些在编译时能被检查的异常称为checked exceptions
,其他为unchecked exceptions
。unchecked exceptions
类有两种:RuntimeException及它的子类
、 Error及它的子类
,其它的异常类均为checked exception
类。
异常对象的产生
异常对象代表所发生的异常,但不是异常本身。异常对象可能由系统被动产生,也可以由程序主动产生。
在Java语言中,异常机制是:一旦出现异常,可以由运行的方法(主动)或JVM(被动)生成一个异常对象,它代表该异常事件的类型。异常对象从产生和被传递提交给Java运行系统的过程称为抛出异常(throw)。你可以抛出任何类型的异常。
比如引用 t,在使用t之前,我们应该检查t是否真正地引用某个实例,如下:
if(t == null)
throw new NullPointerException();//程序产生
JDK中所有异常类的构造方法有两类,一类是缺省构造方法,一类是带有字符串参数的构造方法:throw new NullPointerException("t = null");
throw
把new
所产生的引用抛出,由catch
捕获,进行处理。
当发生异常时,会有下面的过程:首先,创建(系统或程序)异常对象代表异常,然后当前的执行流程中断,异常处理机制接管并且寻找合适的代码以继续执行程序,这段代码就是exception handler。这如同生产线上,当某个产品被发现有某种缺陷时,会被质检员贴上某种描述该缺陷的标签(这个标签类比为异常对象),然后撤出该产品并根据标签的类别,该产品会被发送到不同的部门进行相应的处理(相当于是exception handler)。
异常的处理
try...catch语句
语句格式为:
try {
// The guarded region: Dangerous activities
// that might throw A, B, or C
} catch(A a1) {
// Handler for situation A
} catch(B b1) {
// Handler for situation B
} catch(C c1) {
// Handler for situation C
} finally {
// Activities that happen every time
}
每个catch语句就像一个小的方法,它接受一个参数,而且只能是一个参数,就像方法的参数。该参数的类型已经给出了处理异常的足够信息。如果抛出了一个异常,异常处理机制就在catch中依次寻找,就近匹配,子类异常会匹配父类异常,直到找到匹配的异常为止,然后进入该catch语句中。
注意,在try中可能有很多语句会产生相同的异常,但是在对应的catch只需要一个。
有时候,不管try中有没有异常的发生都要执行一些特定的语句,那么这些语句可以放在finally语句中。所以通常在程序中为了保证某资源一定会释放,通常把释放资源的语句放在finally语句块中。
在finally语句块中的代码通常是一定会执行的。但是在执行finally语句块之前退出了JVM,则finally语句块不会执行。
try{
//退出JVM
System.exit(0);
}finally{
//不会执行.
System.out.println("finally....");
}
示例:
import java.io.*;
public class testE extends Exception{
static void f1() throws testE{
try{
throw new testE();
}catch (ArrayIndexOutOfBoundsException e){
System.out.println("from f1");
}
}
public static void main(String[] args){
try{
f1();
}
catch(testE e){
System.out.println("from main.");
}
}
}
异常表
在方法内部如果产生异常但没有处理,则需要抛出该方法体,该方法需要使用异常表。异常表指定某个方法可能抛出的异常的种类。使用方法如下:
void f() throws TooBig, TooSmall, DivZero { //...
下例
void f() { // ...
意味着该方法不会抛出任何异常 (除了RuntimeException异常或那些继承自它的异常),方法可能产生的所有异常,异常表中都应列出(除了RuntimeException异常或那些继承自它的异常),但异常表中的出现的异常,方法在实际执行中可以没有发生。
下例:
//编译无法通过.
//IOException没有处理
public static void main(String[] args) throws FileNotFoundException{
FileInputStream fis = new FileInputStream("abc");
fis.read();
}
所有RuntimeException类及其子类的异常系统均可以自动处理,同时RuntimeException 异常及子类异常无需出现在异常表中,而且这种异常既可以通过catch捕获,也可以不处理。记住:你只能忽略RuntimeException (或它的子类).
public class NeverCaught {
static void f() {
throw new RuntimeException("From f()");
}
static void g() {
f();
}
public static void main(String[] args) {
g();
}
}
自定义异常类
JDK异常没有包括所有可能的异常情况,所以你可以写出自己定义的异常类来。为创建自己的异常,你应该继承一个在意义上和该异常相似的异常类。
例如:
class SimpleException extends Exception {}
public class SimpleExceptionDemo {
public void f() throws SimpleException {
System.out.println("Throw SimpleException from f()");
throw new SimpleException();
}
public static void main(String[] args) {
SimpleExceptionDemo sed = new SimpleExceptionDemo();
try {
sed.f();
} catch(SimpleException e) {
System.err.println("Caught it!");
}
}
}
又例如:
class MyException extends Exception {
public MyException() {}
public MyException(String msg) { super(msg); }
}
public class FullConstructors {
public static void f() throws MyException {
System.out.println("Throwing MyException from f()");
throw new MyException();
}
public static void g() throws MyException {
System.out.println("Throwing MyException from g()");
throw new MyException("Originated in g()");
}
public static void main(String[] args) {
try {
f();
} catch(MyException e) {
System.out.println(e);
}
try {
g();
} catch(MyException e) {
System.out.println(e);
}
}
}
//: ExtraFeatures.java
// Further embellishment of exception classes.
class MyException2 extends Exception {
private int x;
public MyException2() {}
public MyException2(String msg) { super(msg); }
public MyException2(String msg, int x) {
super(msg);
this.x = x;
}
public int val() { return x; }
}
public class ExtraFeatures {
public static void f() throws MyException2 {
System.out.println("Throwing MyException2 from f()");
throw new MyException2();
}
public static void g() throws MyException2 {
System.out.println("Throwing MyException2 from g()");
throw new MyException2("Originated in g()");
}
public static void h() throws MyException2 {
System.out.println("Throwing MyException2 from h()");
throw new MyException2("Originated in h()", 47);
}
public static void main(String[] args) {
try {
f();
} catch(MyException2 e) {
e.printStackTrace();
}
try {
g();
} catch(MyException2 e) {
e.printStackTrace();
}
try {
h();
} catch(MyException2 e) {
e.printStackTrace();
System.err.println("e.val() = " + e.val());
}
}
}
如果需要捕获任意异常,代码如下:
catch(Exception e) {
System.err.println("Caught an exception");
}
上面的语句应该放在异常处理语句的最后。
一些例子:
public class ExceptionTest09{
public static void main(String[] args){
int i = m1();
System.out.println(i); //10
}
public static int m1(){
int i = 10;
try{
return i;
}finally{
i++;
System.out.println("m1的i=" + i); //11
}
}
}
/*
重写的方法不能比被重写的方法抛出更宽泛的异常.
*/
import java.io.*;
/*
class A{
public void m1(){}
}
class B extends A{
//子类永远无法抛出比父类更多的异常.
public void m1() throws Exception{}
}
*/
class A{
//public void m1() throws FileNotFoundException{}
public void m1() throws IOException{}
}
class B extends A{
//public void m1() throws IOException{}
public void m1() throws FileNotFoundException{}
}