java异常处理字符串类型信息

Posted Monkey_Dog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java异常处理字符串类型信息相关的知识,希望对你有一定的参考价值。

一、异常处理

java的理念是结构不佳的的代码不能运行。代码的健壮性来自各个构件的健壮性,我们需要把异常想象成事务,其实它就是能把每一件事都当作了一个事务来处理

一般抛出异常可以这样,throw new NullPointerException,或者可以给他更添加参数throw new NullPointerException("t=null").。

java.lang.Throwable

java.lang.Exception

java.lang.RuntimeExcepiton(不受检查的异常)

......(受检查异常)

java.lang.Error

Error 是 Throwable 的子类,表示仅靠程序本身无法恢复的严重错误,用于指示合理的应用程序不应该试图捕获的严重问题。
在执行该方法期间,无需在方法中通过throws声明可能抛出但没有捕获的 Error 的任何子类,因为Java编译器不去检查它,也就是说,当程序中可能出现这类异常时,即使没有用try...catch语句捕获它,也没有用throws字句声明抛出它,还是会编译通过

1、一般异常处理分为终止模型和恢复模型,我们使用的一般是终止模型,因为:恢复性的处理程序需要了解异常抛出的地点,这势必包含依赖于抛出位置的非通用性代码,增加了代码维护和编写的困难,有一种做法是在while里面不停地try,直到到满意的结果

2、创建自定义异常需要继承Exception。继承后可以重写getMessage()方法,他会在e.printStackTrack(System.out)的时候输出更加详细的信息,相当于toString()方法

3、异常说明:你可能开发一个程序库,但并没有与源代码一起发布,在异常抛出的时候客户端程序猿无法查看具体的异常相关信息,java提供了异常说明的方法去告知客户端程序猿某个方法会抛出的异常类型,例如:void f() throws XXXException{...} ,或者说对于处理不了的异常或者要转型的异常,在方法的声明处通过throws语句抛出异常。我们看看java处理异常的步骤:


如果每个方法都是简单的抛出异常,那么在方法调用方法的多层嵌套调用中,Java虚拟机会从出现异常的方法代码块中往回找,直到找到处理该异常的代码块为止。然后将异常交给相应的catch语句处理。如果Java虚拟机追溯到方法调用栈最底部main()方法时,如果仍然没有找到处理异常的代码块,将按照下面的步骤处理:
        第一、调用异常的对象的printStackTrace()方法,打印方法调用栈的异常信息。
        第二、如果出现异常的线程为主线程,则整个程序运行终止;如果非主线程,则终止该线程,其他线程继续运行。

        ——越早处理异常消耗的资源和时间越小,产生影响的范围也越小。因此,不要把自己能处理的异常也抛给调用者。
        ——finally语句在任何情况下都必须执行的代码,这样可以保证一些在任何情况下都必须执行代码的可靠性。finally语句唯一不被执行的情况是方法执行了System.exit()方法。System.exit()的作用是终止当前正在运行的 Java 虚拟机。finally语句块中不能通过给变量赋新值来改变return的返回值,也建议不要在finally块中使用return语句,没有意义还容易导致错误。当抛出异常时,throw之后的语句是不会被执行的


4、被检查异常、不被检查异常:继承自RuntimeException的异常是不被检查异常,例如空指针错误,java不可能让每一个引用都让程序猿亲自来检测是否为null,你也可以想象在java虚拟机运行的时候可能会出现各种异常,可能是你无法控制的错误,这类运行时的异常如果处处都使用try的形式检查,程序规模该是怎样地大。所以这类异常是不必要使用try的形式来捕获的,称为不被检查异常,比如你需要编写一个工具类,你不确定调用者会不会传入null,所以你写了很多if语句来判断参数是否为null,仔细想想:这是否真的有必要?提供参考stackOverflow的一个回答:http://www.importnew.com/13002.html

针对这种情况,我们会见过空对象这一个概念:这里举一个例子使用空对象(公司填充职位)一般空对象都是单例

public interface Null {}

public class Person {
	public final String first;
	public final String last;
	public final String address;
	public Person(String first, String last, String address) {
		super();
		this.first = first;
		this.last = last;
		this.address = address;
	}
	
	@Override
	public String toString() {
		return "Person:"+first+" "+last+" "+address;
	}
	
	//利用嵌套类描述一个空的Person
	//注意嵌套类如果没有被外围类使用到是不会被初始化的
	public static class NullPerson extends Person implements Null{
		public NullPerson() {
			super("None", "None", "None");
		}
		@Override
		public String toString() {return "NullPerson";}
	}
	public static final Person Null = new NullPerson();
}

//我们分别在各个构造方法、setPerson方法里面检查并放入NULL对象
public class Position {
	private String title;
	private Person person;
	public Position(String title, Person person) {
		this.title = title;
		this.person = person;
		if(person==null){
			//写到这里突然想到内部类的初始化问题
			//顺便记一下,如果不是嵌套类不能这么new:person = new Person.NullPerson();
			person = Person.Null;
		}
	}
	public Position(String title){
		this.title = title;
		person = Person.Null;
	}
	
	public String getTitle() {return title;}
	public void setTitle(String title) {this.title = title;}
	public Person getPerson() {return person;}
	public void setPerson(Person person) {
		this.person = person;
		if(person==null){person = Person.Null;}
	}
	public String toString(){return "Position:"+title+" "+person+"\\n";}
}

//这个是位置类,他包括一系列的位置,
public class Staff extends ArrayList<Position> {
	public void add(String title,Person person){
		add(new Position(title,person));
	}
	public void add(String... titles){
		for(String title:titles){add(new Position(title));}
	}
	//添加位置
	public Staff(String... titles){add(titles);}
	//判断这个位置是否可用
	public boolean positionAvailable(String title){
		for(Position position:this){
			if(position.getTitle().equals(title)&&
					position.getPerson() == Person.Null){
				return true;
			}
		}
		return false;
	}
	
	public void fillPosition(String title,Person hire){
		for(Position position:this){
			if(position.getTitle().equals(title) &&
					position.getPerson() == Person.Null){
				position.setPerson(hire);
				return;
			}
		}
		throw new RuntimeException(
				"Position"+title+"not available");
	}
	
	public static void main(String[] args) {
		//首先创造出位置
		Staff staff = new Staff("President","CTO","Marketing Manager"
				,"Product Manager","Product Lead","Software Engineer"
				,"Software Engineer","Software Engineer","Software Engineer"
				,"Test Engineer","Technical Writer");
		//填充位置
		staff.fillPosition("President", new Person("Me","Last","The Top,lonely At"));
		staff.fillPosition("Product Lead", new Person("Janet","Planner","The Burbs"));
		if(staff.positionAvailable("Software Engineer")){
			staff.fillPosition("Software Engineer", new Person("Bob","Coder","Bright Light City"));
		}
		//输出位置
		System.out.println(staff);
	}
}
//[Position:President Person:Me Last The Top,lonely At
//, Position:CTO NullPerson
//, Position:Marketing Manager NullPerson
//, Position:Product Manager NullPerson
//, Position:Product Lead Person:Janet Planner The Burbs
//, Position:Software Engineer Person:Bob Coder Bright Light City
//, Position:Software Engineer NullPerson
//, Position:Software Engineer NullPerson
//, Position:Software Engineer NullPerson
//, Position:Test Engineer NullPerson
//, Position:Technical Writer NullPerson
//]
这仅仅是空对象的一个例子(有时候也把这种算上一种模式,适当使用空对象会剩下很多时候检查null情况的精力)

既然提到了空对象,不妨再扯远一点——利用到类型信息,下面也会总结一下类型信息的、命令模式、动态代理的一个扫雪机器人例子

public interface Null {}

//如果看作命令模式,会发现这里的接口其实充当的就是一个Command接口
public interface Operation {
	String description();
	//execute
	void command();
}

//实际执行者
public interface Robot {
	String name();
	String model();
	List<Operation> operations();
	//利用嵌套类进行测试
	class Test{
		public static void test(Robot r){
			if(r instanceof Null){System.out.println("[Null Robot]");}
			System.out.println("Robot name:"+r.name());
			System.out.println("Robot model:"+r.model());
			for(Operation operation:r.operations()){
				System.out.println(operation.description());
				operation.command();
			}
		}
	}
}
尽管看起来命令模式并不是那么严格,同时这里我们可以利用嵌套类测试一下,结果写在了注释语句上

注意这时候还没使用动态代理去创建空对象

public class SnowRemovalRobot implements Robot {
	private String name;
	public SnowRemovalRobot(String name) {this.name = name;}
	@Override
	public String name() {return name;}
	@Override
	public String model() {return "SnowBot Series 11";}
	@Override
	public List<Operation> operations() {
		return Arrays.asList(
				new Operation(){
					@Override
					public String description() {return name+"can shovel snow";}
					@Override
					public void command() {System.out.println(name+"shoveling snow");}
				},
				new Operation() {
					@Override
					public String description() {return name+"can chip ice";}
					@Override
					public void command() {
						System.out.println(name+"chipping ice");
					}
				},
				new Operation() {
					@Override
					public String description() {return name+"can clear the roof";}
					@Override
					public void command() {
						System.out.println(name+"clearing roof");
					}
				});
	}
	public static void main(String[] args) {
		Robot.Test.test(new SnowRemovalRobot("Slusher"));
	}
}
//Robot name:Slusher
//Robot model:SnowBot Series 11
//Slushercan shovel snow
//Slushershoveling snow
//Slushercan chip ice
//Slusherchipping ice
//Slushercan clear the roof
//Slusherclearing roof
假设存在许多不同的Robot,我们想对每一种的Robot都创建一个空对象去执行某些操作,这里的动态代理不做过多解释(忘掉了),后面会在另一篇博文总结

public class NullRobotProxyHandler implements InvocationHandler {
	private String nullName;
	private Robot proxied = new NRobot();
	public NullRobotProxyHandler(Class<? extends Robot> type) {
		nullName = type.getSimpleName()+"NullRobot";
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		//invoke的却是NRobot这个对象
		return method.invoke(proxied, args);
	}
	
	private class NRobot implements Null,Robot{
		@Override
		public String name() {return nullName;}
		@Override
		public String model() {return nullName;}
		@Override
		public List<Operation> operations() {return Collections.emptyList();}	
	}
}

public class NullRobot {
	public static Robot newNullRobot(Class<? extends Robot> type){
		return (Robot)Proxy.newProxyInstance(NullRobot.class.getClassLoader(),
				new Class[]{Null.class,Robot.class},
				new NullRobotProxyHandler(type));
	}
	
	public static void main(String[] args) {
		Robot[] bots = {
				new SnowRemovalRobot("SnowBee"),
				//如果需要一个空Robot对象,只需要这样子调用,这使用类字面常量创建实例
				newNullRobot(SnowRemovalRobot.class)
		};
		
		for(Robot bot:bots){
			Robot.Test.test(bot);
		}
	}
}

输出结果:

Robot name:SnowBee
Robot model:SnowBot Series 11
SnowBeecan shovel snow
SnowBeeshoveling snow
SnowBeecan chip ice
SnowBeechipping ice
SnowBeecan clear the roof
SnowBeeclearing roof
[Null Robot]
Robot name:SnowRemovalRobotNullRobot
Robot model:SnowRemovalRobotNullRobot

小插曲到此结束,写的不是很好,因为对知识点的掌握并不是很好,继续正题

5、try、catch语句中,catch语句捕获的异常必须从小到大排列,因为程序是先从子类检查是否是这样子的异常,然后再向上一级检查,一旦捕获,就不再检查

6、如果在catch或者在try中执行了return语句,则都会先执行了finally的语句,再return,上面也提到过,如果是System.exit就不会执行finally

7、我们可以在捕获一个异常后抛出另外一个异常,并且希望把原来的异常信息保存下来,这被称为异常链,通俗来说,异常链就是把原始的异常包装为新的异常类,并在新的异常类中封装了原始异常类,这样做的目的在于找到异常的根本原因

8、finally需要执行清理工作时,如果在catch中有FileNotFoundException之类的异常,说明文件还没被打开,finally是不适宜使用close去关闭文件的,这种情况应该在catch下,打开文件的情况下关闭

9、可以通过如下方式把被检查异常“屏蔽”:

try{
	switch(type){
		case 0:throw new FileNotFoundException();
		case 1:throw new IOException();
		case 2:throw new RuntimeException();
		default:return;
	}catch(Exception e){
		throw new RuntimeException(e);
	}
}

//可以使用e.getCause()来获取具体类型的异常,然后catch出来
10、使用原则:

(1)、知道如何处理才捕获异常

(2)、解决问题并且重新调用产生异常的方法

(3)、进行少许修补,然后绕过异常发生的地方继续执行

(4)、用别的数据进行计算,以代替方法预计会返回的值

(5)、把当前运行环境下能做的事情尽量做完,然后把相同的异常抛到更高层

(6)、把当前运行环境下能做的事情尽量做完,然后把不同的异常抛到更高层

(7)、终止程序

(8)、进行简化

(9)、让类库更加安全

二、字符串

在总结之前,先看一下以前看过的一个例子:

public class Main {
    public static void main(String[] args) {
        String str1 = "hello world";
        String str2 = new String("hello world");
        String str3 = "hello world";
        String str4 = new String("hello world");
        System.out.println(str1==str2);//false
        System.out.println(str1==str3);//true
        System.out.println(str2==str4);//false
    }
}
有需要可以看看jvm的内存机制: 在str1、str3这里,在创建了str1之后,内容被保存在了常量池,当创建str3的时候,jvm会先找常量池的内容是否有相同,而new的方式是在堆里面,当然不是相同的一份

1、String、StringBuilder,总的来说使用后者更高效,如果在循环里面使用String,最好换成StringBuilder(注意是循环里面,这并不意味所有情况StringBuilder比String更高效),使用appended来拼接字符串,尽管编译器会做一定的优化。例如:

public class UsingStringBuilder {
	public static Random rand = new Random(47);
	public String toString(){
		StringBuilder result = new StringBuilder("[");
		for(int i = 0;i<25;i++){
			result.append(rand.nextInt(100));
			result.append(", ");
		}
		result.delete(result.length()-2, result.length());
		result.append("]");
		return result.toString();
	}
	
	public static void main(String[] args) {
		UsingStringBuilder usb = new UsingStringBuilder();
		System.out.println(usb.toString());
	}
}

不能这样子做:append(a+":"+c);这样编译器会为你创建另一个StringBuilder

其次,String类型的数据在操作的时候,例如replace方法,他返回的是一个新串,而不是对原来的串操作:

String x="Strings Are ";
String s=x;

x+=" Immutable Objects!";
System.out.println("x= "+x);
System.out.println("s= "+s);

代码串输出:

x= Strings Are Immutable Objects

s= Strings Are

“对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”

如果要重写toString方法,不应该在里面返回的串存在this这种关键字,因为当编译器把this转换成String的时候,会调用他的toString方法,这就是一个无意识的递归行为,代替方法:改为super().toString();其实我们一般在重写toString方法的时候,笔者是使用eclipse的,可以看到编译器自动生成的代码有super().toString()这样的语句

——String str = "hello"+ "world"的效率就比 StringBuilder st  = new StringBuilder().append("hello").append("world")要高。直接拼接来说,String的效率是较高的,如果经常对字符串本身进行操作,使用StringBuilder较好。

关于StringBuilder、String、StringBuffer的区别,或者除了重载+、直接使用引用相加的情况字符串是否相等等情况可以参考http://www.cnblogs.com/dolphin0520/p/3778589.html

这个结论摘自上面这篇文章:

1)对于直接相加字符串,效率很高,因为在编译器便确定了它的值,也就是说形如"I"+"love"+"java"; 的字符串相加,在编译期间便被优化成了"Ilovejava"。这个可以用javap -c命令反编译生成的class文件进行验证。
      对于间接相加(即包含字符串引用),形如s1+s2+s3; 效率要比直接相加低,因为在编译器不会对引用变量进行优化。

2)String、StringBuilder、StringBuffer三者的执行效率:
      StringBuilder > StringBuffer > String
      当然这个是相对的,不一定在所有情况下都是这样。
      比如String str = "hello"+ "world"的效率就比 StringBuilder st  = new StringBuilder().append("hello").append("world")要高。
      因此,这三个类是各有利弊,应当根据不同的情况来进行选择使用:
      当字符串相加操作或者改动较少的情况下,建议使用 String str="hello"这种形式;
      当字符串相加操作较多的情况下,建议使用StringBuilder,如果采用了多线程,则使用StringBuffer


只要理解了final在编译器会得到优化,以下的例子就不难理解

String a = "hello2";   
String b = "hello" + 2;   
System.out.println((a == b));
true


String a = "hello2";    
String b = "hello";//编译期无法确定b的值       
String c = b + 2;//符号引用存在       
System.out.println((a == c));
false


String a = "hello2";     
final String b = "hello";//编译期可以确定       
String c = b + 2;       
System.out.println((a == c));
true


String a = "hello2"; 
final String b = getHello();//编译期不能确定它的值,所以不能优化b为hello
String c = b + 2; 
System.out.println((a == c));
public static String getHello() {return "hello";}
false


//intern方法会在运行时常量池中查找是否存在内容相同的字符串,如果存在则返回指向该字符串的引//用,如果不存在,则会将该字符串入池,并返回一个指向该字符串的引用
String a = "hello";
String b =  new String("hello");
String c =  new String("hello");
String d = b.intern();


System.out.println(a==b);//false
System.out.println(b==c);//false
System.out.println(b==d);//false
System.out.println(a==d);//true


2、格式化字符串:

System.out.format(PrintStream/PrintWriter):System.out.format(“[%d %f]\\n”,x,y) ;x=5,y=0.01输出[5 0.01]

(1、Formatter类:例如,用new Formatter(System.out)的方式创建,f.format("%s %d %d",s,x,y) s=name,x=1,y=2输出name 1 2,Formatter也包含了一些转换类型,并且对空格与对齐提供强大的控制能力

(2、String.format():静态方法,实质上也是使用了Formatter

3、java使用String.format实现的十六进制转换工具:

//二进制转换十六进制
	public static String format(byte[] data){
		StringBuilder result = new StringBuilder();
		int n = 0;
		for(byte b:data){
			if(n%16==0) result.append(String.format("%0.5X:", n));
			result.append(String.format("%02X:", n));
			n++;
			if(n%16==0) result.append("\\n");
		}
		result.append("\\n");
		return result.toString();
	}
样例输出:00000:CA FE BA BE 00 00 00 31 00......
4、正则表达式:http://blog.csdn.net/yysyangyangyangshan/article/details/7105691

字符
B指定字符B
\\xhh十六进制为oxhh的字符
\\uhhhh十六进制表示为oxhhhh的Unicode字符
\\t制表符Tab
\\n换行符
\\r回车
\\f换页
\\e转义

字符类
[abc]包含a、b、c的任何字符
[^abc]除了a、b、c之外的任何字符
[a-zA-Z]从a到z或从A到Z的任何字符
[abc[hij]]任意a、b、c、h、i、j
[a-z&&[hij]]任意h、i、j(交)
\\s空白符,空格、tab、换行、换页、回车
\\S非空白符
\\d数字0-9
\\D非数字0-9
\\w词字符a-zA-Z0-9
\\W非词字符

除此之外,还有逻辑操作符、边界匹配符、编译标记等


关于字符串知识比较繁杂,最好还是直接使用例子来理解,提供几个例子作为参考:


//测试正则表达式,是否匹配一个输入字符串
//命令行输入字符串:abcabcabcdefabc "abc+" "(abc)+" "(abc){2,}"
//输出:
//Input:"abcabcabcdefabc"
//Regular expression:"abcabcabcdefabc"
//Match "abcabcabcdefabc" at position 0-14
//Regular expression:"abc+"
//Match "abc" at position 0-2
//Match "abc" at position 3-5
//Match "abc" at position 6-8
//Match "abc" at position 12-14
//Regular expression:"(abc)+"
//Match "abcabcabc" at position 0-8
//Match "abc" at position 12-14
//Regular expression:"(abc){2,}"
//Match "abcabcabc" at position 0-8
public class TestRegularExpress {
	public static void main(String[] args) {
		if(args.length<2){
			System.out.println("Usage:\\njava TestRegularExpress "+
					"characterSequence regularExpression");
			System.exit(0);
		}
		
		System.out.println("Input:\\""+args[0]+"\\"");
		for(String arg:args){
			System.out.println("Regular expression:\\""+arg+"\\"");
			Pattern p = Pattern.compile(arg);
			Matcher m = p.matcher(args[0]);
			while(m.find()){
				System.out.println("Match \\""+m.group()+"\\" at position "+
						m.start()+"-"+(m.end()-1));
			}
		}
	}
}


Matcher.find()

public class Finding {
	public static void main(String[] args) {
		Matcher m = Pattern.compile("\\\\w+").matcher("Evening is full of the linnet's wings");
		//这里输出每个单词
		while(m.find()){
			System.out.println(m.group()+" ");
		}
		int i = 0;
		//这里输出每个单词之后,还输出单词里面的每个字母
		while(m.find(i)){
			System.out.println(m.group()+" ");
			i++;
		}
	}
}


正则表达式组:组是用货号划分的正则表达式,可以根据组的编号来引用某个组

public class Groups {
	static public final String POEM = 
			"Twas brillig, and the slithy toves\\n"+
			"Did gyre and gimble in the wabe.\\n"+
			"All mimsy were the borogoves,\\n"+
			"And the mome raths outgrabe.\\n"+
			"Beware the Jabberwock, my son,\\n"+
			"The jaws that bite, the claws that catch.\\n"+
			"Beware the Jubjub bird, and shun\\n"+
			"The frumious Bandersnatch.";
	public static void main(String[] args) {
		Matcher m = Pattern.compile("(?m)(\\\\S+)\\\\s+((\\\\S+)\\\\s+(\\\\S+))$").matcher(POEM);
		while(m.find()){
			for(int j = 0;j<m.groupCount();j++)
				System.out.print("["+m.group(j)+"]");
			System.out.println();
		}
	}
//	[the slithy toves][the][slithy toves][slithy]
//	[in the wabe.][in][the wabe.][the]
//	[were the borogoves,][were][the borogoves,][the]
//	[mome raths outgrabe.][mome][raths outgrabe.][raths]
//	[Jabberwock, my son,][Jabberwock,][my son,][my]
//	[claws that catch.][claws][that catch.][that]
//	[bird, and shun][bird,][and shun][and]
//	[The frumious Bandersnatch.][The][frumious Bandersnatch.][frumious]
}


split()

public class SplitDemo {
	public static void main(String[] args) {
		String input = "This!!unusual use!!of exclamation!!points";
		System.out.println(Arrays.toString(Pattern.compile("!!").split(input)));
		System.out.println(Arrays.toString(Pattern.compile("!!").split(input,3)));
//		[This, unusual use, of exclamation, points]
//		[This, unusual use, of exclamation!!points]
	}
}


扫描输入

public class SimpleRead {
	public static BufferedReader input = new BufferedReader(
			new StringReader("Sir Robin of Camelot\\n22 1.61803"));
	public static void main(String[] args) throws IOException {
		System.out.println("What is your name?");
		String name = input.readLine();
		System.out.println(name);
		
		System.out.println("How old are you? What is your favorite double");
		System.out.println("input:<age> <double>");
		String numbers = input.readLine();
		System.out.println(numbers);
		
		String[] numArray = numbers.split(" ");
		int age = Integer.parseInt(numArray[0]);
		double favorite = Double.parseDouble(numArray[1]);
		System.out.format("Hi %s.\\n",name);
		System.out.format("In 5 years you will be %d\\n", age+5);
		System.out.format("My favorite double is %f\\n", favorite/2);
	}
	
//	What is your name?
//	Sir Robin of Camelot
//	How old are you? What is your favorite double
//	input:<age> <double>
//	22 1.61803
//	Hi Sir Robin of Camelot.
//	In 5 years you will be 27
//	My favorite double is 0.809015
}

用正则表达式扫描:
public class ThreatAnalyzer {
	static String threatData = "58.27.82.161@02/10/2005\\n"+
			"204.45.234.40@02/11/2005\\n"+
			"58.27.82.161@02/11/2005\\n"+
			"58.27.82.161@02/12/2005\\n"+
			"58.27.82.161@02/12/2005\\n"+
			"[Next log section with different data format]";
	public static void main(String[] args) {
		Scanner scanner = new Scanner(threatData);
		String pattern = "(\\\\d+[.]\\\\d+[.]\\\\d+[.]\\\\d+)@"+"(\\\\d{2}/\\\\d{2}/\\\\d{4})";
		while(scanner.hasNext(pattern)){
			scanner.next(pattern);
			MatchResult match = scanner.match();
			String ip = match.group(1);
			String date = match.group(2);
			System.out.format("Thread on %s from %s\\n", date,ip);
		}
	}
}
//Thread on 02/10/2005 from 58.27.82.161
//Thread on 02/11/2005 from 204.45.234.40
//Thread on 02/11/2005 from 58.27.82.161
//Thread on 02/12/2005 from 58.27.82.161
//Thread on 02/12/2005 from 58.27.82.161


三、类型信息

1、RTTI:(Run-Time Type Identification),通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。(在运行时,识别一个对象的类型)

注意RTTI跟反射不一样:java在运行时识别对象和信息提供的两种方式就是RTTI和反射。例如有这样一个容器:List<Pet>,容器只是把Pet对象当作Object持有,当从list里面取出元素时,java会将结果转化为Pet类型,这就是一种RTTI。又比如:

abstract class Shape {
    // this 调用当前类的toString()方法,返回实际的内容
    void draw(){ System.out.println(this + "draw()"); }
    // 声明 toString()为abstract类型,强制集成在重写该方法
    abstract public String toString();
}

class Circle extends Shape {
    public String toString(){ return "Circle"; }
}

class Square extends Shape {
    public String toString(){ return "Square"; }
}

class Triangle extends Shape {
    public String toString(){ return "Triangle"; }
}

public static void main(String[] args){
    // 把Shape对象放入List<Shape>的数组的时候会向上转型为Shape,从而丢失了具体的类型信息
    List<Shape> shapeList = Arrays.asList(new Circle(), new Square(), new Triangle());
    // 从数组中取出时,这种容器,实际上所有的元素都当成Object持有,会自动将结果转型为Shape,这就是RTTI的基本的使用。
    for(Shape shape : shapeList){
        shape.draw();
    }
}
//Circledraw()
//Squaredraw()
//Triangledraw()

RTTI有一个限制,就是编译时编译器知道这个类型(.class),而反射是在运行的时候检查这个文件,反射并没有什么特别之处,当通过反射与一个未知类型的对象打交道的时候,jvm只是简单的检查这个对象

java在类的加载的时候,先检查是否已经加载过需要加载的类,如果没有,默认类加载器会根据类名来查找.class文件,一旦某个类的Class对象被载入内存,他就被用来创建这个类的对象,例如:Class.forName("类名");的方式去创建一个对象

2、利用Class的例子:

public interface HasBatteries {}
public interface Shoots {}
public interface Waterproof {}

public class Toy {
	Toy(){}
	Toy(int i){}
}

public class FancyToy extends Toy implements HasBatteries,Waterproof,Shoots{
	public FancyToy() {
		super(1);
	}
}

public class ToySet {
	static void printInfo(@SuppressWarnings("rawtypes") Class cc){
		System.out.println("Class name: "+cc.getName()+
				" is Interface? ["+cc.isInterface()+"]");
		System.out.println("Simple name: "+cc.getSimpleName());
		System.out.println("Canonical name: "+cc.getCanonicalName());
	}
	public static void main(String[] args) {
		Class c = null;
		try {
			c = Class.forName("test.FancyToy");//路径
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			System.exit(1);
		}
		printInfo(c);
		for(Class face:c.getInterfaces()){
			printInfo(face);
		}
		Class up = c.getSuperclass();
		Object obj = null;
		try {
			//相当于“不知道正确类型,但是请求编译器正确地创建他”
			//<strong>父类必须要有默认构造器,否则会抛出IllegalAccessException</strong>
			obj = up.newInstance();
		} catch (InstantiationException e) {
			System.exit(1);
		} catch (IllegalAccessException e) {
			System.exit(1);
		}
		printInfo(obj.getClass());
	}
}

3、类字面常量:Class.forName()的另外一种形式创建引用,即形如:Class c = FancyToy.class;这种风格,这种形式更简单安全,高效(编译时会受到检查,所以不用放在try中)关于类字面常量的初始化特性在另一篇博文关于初始化的里面,这里不写了

我们可以看看两种方式来创建对象:

比如说有一个List:List<Class<? extends Pet>> types;其中Pet是一堆宠物的父类,利用add方法去添加对象:

(1)、String[] typeNames = {"typeinfo.pets.Mutt","typeinfo.pets.Pug",......}其中typeinfo是包名,那么使用的时候:types.add((Class<? extends Pet>)Class.forName(name));

(2)、List<Class<? extends Pet>> types = Collections.unmodifiableList(Array.asList(Pet.class,Dog.class,Cat.class......));

4、泛化的Class引用:

public class GWBClassReference {
	public static void main(String[] args) {
		//Class<?> intClass = int.class;或者用这种形式
		Class intClass = int.class;
		Class<Integer> genericIntClass = int.class;
		//genericIntClass = double.class 由于指定了类型,不能这样写
		genericIntClass = Integer.class;
		intClass = double.class;//没有指定类型,所以能这样“赋值”
		
		//另一种情况
		//Class<Number> genericNumberClass = int.class;
		//看上去是不会错的,因为Integer继承自Number,但是它其实无法工作
		
		//正确方式
		Class<? extends Number> genericNumberClass = int.class;
		genericNumberClass = double.class;
		genericNumberClass = Number.class;
	}
}
(newInstance)方式:需要默认构造器

public class CountedInteger {
	private static long counter;
	private final long id = counter++;
	public String toString(){return Long.toString(id);}
}

public class FilledList<T> {
	private Class<T> type;
	public FilledList(Class<T> type){this.type = type;}
	public List<T> create(int nElements){
		List<T> result = new ArrayList<T>();
		
		try {
			for(int i = 0;i<nElements;i++){
				result.add(type.newInstance());
			}
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		return result;
	}
	public static void main(String[] args) {
		FilledList<CountedInteger> fl = 
				new FilledList<CountedInteger>(CountedInteger.class);
		System.out.println(fl.create(15));
	}
}

//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
newInstance返回的是确切的类型,同时也不能直接使用getSuperClass的方法去定义一个形如Class<SuperClass>声明的类,而是Class<? super sonClass> xx = sonClass.getSuperClass();的形式


5、Class引用转化为具体类型:

public class Building {}
public class House extends Building{}

public class Classcast {
	public static void main(String[] args) {
		Building b = new House();
		Class<House> houseType = House.class;
		House h = houseType.cast(b);
		h = (House)b;
	}
}


6、instance和class的等价性

public class Test { 
    static class Base{} 
    static class Derived extends Base{} 
     
    static void test(Object x){ 
        System.out.println("Testing x of type " + x.getClass()); 
        System.out.println("x instanceof Base " + (x instanceof Base)); 
        System.out.println("x instanceof Derived " + (x instanceof Derived)); 
        System.out.println("Base.isInstance(x) " + (Base.class.isInstance(x))); 
        System.out.println("Derived.isInstance(x) " + (Derived.class.isInstance(x))); 
        System.out.println("x.getClass() == Base.class " + (x.getClass() == Base.class)); 
        System.out.println("x.getClass() == Derived.class " + (x.getClass() == Derived.class)); 
        System.out.println("x.getClass().equals(Base.class) " + (x.getClass().equals(Base.class))); 
        System.out.println("x.getClass().equals(Derived.class) " + (x.getClass().equals(Derived.class))); 
    } 
     
    public static void main(String[] args) { 
        Test.test(new Base()); 
        System.err.println("----------------------------"); 
        Test.test(new Derived()); 
    } 
}

//Testing x of type class Test$Base
//x instanceof Base true
//x instanceof Derived false
//Base.isInstance(x) true
//Derived.isInstance(x) false
//x.getClass() == Base.class true
//x.getClass() == Derived.class false
//x.getClass().equals(Base.class) true
//x.getClass().equals(Derived.class) false
//----------------------------
//Testing x of type class Test$Derived
//x instanceof Base true
//x instanceof Derived true
//Base.isInstance(x) true
//Derived.isInstance(x) true
//x.getClass() == Base.class false
//x.getClass() == Derived.class true
//x.getClass().equals(Base.class) false
//x.getClass().equals(Derived.class) true


7、RTTI和反射:RTTI是编译器在编译时打开和检查.class文件,编译器必须在编译时知道存在.class文件,而反射是并不一定这样,在编译时不可获取,在运行时打开.class文件。因为会存在有实现编译器不知道.class文件的情况,例如远程调用。其中像Class.forName、xxx.class并不是反射,看看一对反射的描述:

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

我们可以看看一个通过反射的类方法提取器:

提取的方法都是public的,其他的不能提取

public class ShowMethods {
	private static String usage = "usage:\\n"+
			"ShowMethods qualified.class.name\\n"+
			"To show all methods in class or:\\n"+
			"ShowMethods qualified.class.name word\\n"+
			"To search for methods involving 'word'";
	private static Pattern p = Pattern.compile("\\\\w+\\\\.");
	public static void main(String[] args){
		if(args.length<1){
			System.out.println(usage);
			System.exit(0);
		}
		int lines = 0;
		try {
			Class<?> c = Class.forName(args[0]);
			Method[] methods = c.getMethods();
			Constructor[] ctors = c.getConstructors();
			if(args.length==1){
				for(Method method:methods){
					System.out.println(p.matcher(method.toString()).replaceAll(""));
				}
				for(Constructor ctor:ctors){
					System.out.println(p.matcher(ctor.toString()).replaceAll(""));
				}
				lines = methods.length+ctors.length;
			}else{
				for(Method method:methods){
					if(method.toString().indexOf(args[1])!=-1){
						System.out.println(p.matcher(method.toString()).replaceAll(""));
						lines++;
					}
				}
				for(Constructor ctor:ctors){
					if(ctors.toString().indexOf(args[1])!=-1){
						System.out.println(p.matcher(ctor.toString()).replaceAll(""));
						lines++;
					}
				}
			}
		} catch (ClassNotFoundException e) {e.printStackTrace();}
	}
}
//public static void main(String[])
//public final void wait() throws InterruptedException
//public final void wait(long,int) throws InterruptedException
//public final native void wait(long) throws InterruptedException
//public native int hashCode()
//public final native Class getClass()
//public boolean equals(Object)
//public String toString()
//public final native void notify()
//public final native void notifyAll()
//public ShowMethods()

既然能提取方法,那么我们也能通过反射在运行时确定一个类并调用他的方法,事先是你知道方法名,但是方法名也能从.class文件中,通过javap -private C得到

package typeinfo.interfacea;

public interface A {
	void f();
}

package typeinfo.packageaccess;
import typeinfo.interfacea.A;
public class C implements A{

	public void g(){System.out.println("public C.g()");}
	@Override
	public void f() {System.out.println("public C.f()");}
	void u(){System.out.println("package C.u()");}
	protected void v(){System.out.println("protected C.v()");}
	private void w(){System.out.println("private C.w()");}
}

package typeinfo.packageaccess;
import typeinfo.interfacea.A;
public class HiddenC {
	public static A makeA(){return new C();}
}

package typeinfo;

import java.lang.reflect.Method;
import typeinfo.interfacea.A;
import typeinfo.packageaccess.C;
import typeinfo.packageaccess.HiddenC;
public class HiddenImplementation {
	public static void main(String[] args) throws Exception {
		A a = HiddenC.makeA();
		a.f();
		System.out.println(a.getClass().getName());

		//利用反射,我们可以通过方法名达到调用所有方法,甚至是private方法
		//即使把接口时限为一个匿名类、私有内部类,反射仍然可以达到目的
		//既有安全性问题,也有解决一些特定问题的好处
		//命令行进入workspace的目录下找到C.class,输入javap -private C命令
		//能得到所有方法的名字
		callHiddenMethod(a, "g");
		callHiddenMethod(a, "u");
		callHiddenMethod(a, "v");
		callHiddenMethod(a, "w");
		
	}
	static void callHiddenMethod(Object a,String methodName)throws Exception{
		Method g = a.getClass().getDeclaredMethod(methodName);
		g.setAccessible(true);
		g.invoke(a);
	}
}
//public C.f()
//typeinfo.packageaccess.C
//public C.g()
//package C.u()
//protected C.v()
//private C.w()





以上是关于java异常处理字符串类型信息的主要内容,如果未能解决你的问题,请参考以下文章

java.util.MissingResourceException: Can't find bundle for base name init, locale zh_CN问题的处理(代码片段

Java异常如何解决

Java中的异常处理机制

Java异常处理机制

java 反射代码片段

java异常处理