Java成神之路-Java异常、集合、IO流、多线程和反射
Java异常处理
一、异常概述
异常:Exception,是在运行发生的不正常情况。
原始异常处理:
代码阅读性差,臃肿不堪,与正常流程代码结合的很紧密,所以,在JAVA中进行一系列的改良,将一系列常见的问题,用面向对象的思考方式,对其进行了描述、封装。
在JAVA中,用类的形式对不正常情况进行了描述和封装对象。当程序出现问题时,调用相应的处理办法。
描述不正常情况的类,就称为异常类。将流程代码和异常代码进行分离。
异常就是JAVA通过面向对象的思想,将问题封装成了对象。用异常类对其进行描述。不同的问题,用不同的类进行描述。那么意味着,问题有多少,类就有多少。
二、异常体系
问题很多,意味着描述的类也很多,将其共性进行向上抽取,就形成了异常体系。最终异常分为两大类:
Throwable(父类):问题发生,就应该抛出,让调用者处理。该体系的特点就在于Throwable及其子类都具有可抛性。
两个关键字实现可抛性:throws、throw
|–1.一般不可处理的。Error(错误)
特点:是由JVM(java虚拟机)抛出的严重性的问题。这种问题发生,一般不针对性处理,直接修改程序。
|–2.可以处理的。Exception(异常)
特点:子类的后缀名都是用其父类名作为后缀,阅读性很强。
三、异常-原理&异常对象的抛出throw
可以看出,异常时,底层throw直接调用异常方法,抛出异常,只不过这些都在底层完成,我们看不到而已。
JAVA虚拟机它有一套异常处理机制,就是会把异常的各种信息,位置等报出来,以供解决异常。
真正开发的时候,这些异常信息是不会直接报出来的,会存成日志,我们定期查看。而且这个异常信息给用户也没用,只有给我们才有用。
四、异常-自定义异常&异常类的抛出throws
自定义异常:JAVA给出的一堆现有的异常没有我们需要的,这时候可以自定义了。但是这个类一定要继承Exception类。
五、异常-编译时检测异常和运行时异常的区别&throw和throws的区别
Exception体系分两种:
1.一种是编译时被检测异常(throws)。除runtimeException子类的所有子类。这样的问题可以针对性的处理。
2.运行时异常(throw)。Exception的子类中runtimeException和其子类。这种问题一般不处理,直接编译通过,在运行时让调用时的程序强制停止。
六、异常-异常捕捉try-catch
异常处理的捕捉形式:具体格式:
建立日志文件:第三方插件-log4j
七、异常-多catch情况
一个try对应多个catch的时候,小细节:
当多catch需要存在catch(Exception e)的时候,需要放到最后,不然会挂,因为Exception为父类,能接收所有的异常,放它之后,其他的就多余了,所以,它要放在最后的catch。
八、异常-异常处理原则
异常就是问题,JAVA对一些常见的问题已经弄好了,拿来用就好了。
如果,个别问题只在你自己的项目里出现,并且JAVA里没有这类问题,那就需要自己描述该问题。
1.方法内如果抛出需要检测的异常,那么方法上必须要声明,否则必须在方法内用try-catch捕捉,否则编译失败。
2.如果调用了声明异常的函数,要么try-catch要么throws,否则编译失败。
3.什么时候catch,什么时候throws?功能内容可以解决,用catch,解决不了,用throws告诉调用者,有调用者解决。
4.如果一个功能抛出了多个异常,那么调用时必须有对应多个catch进行针对性的处理。
九、异常-finally代码块
finally为一定会执行的代码,只有一种情况,finally不会执行。
try-catch-finally代码块组合特点:
1.try-catch-finally常见组合体
2.try-catch(可以多个catch)没有finally,没有资源需要释放(关闭),可以不用finally。
3.try-finally,没有catch时,方法旁边需要throws声明,因为没catch没处理。异常无法直接catch处理,但是资源需要关闭,这时用此组合。
十、异常的注意事项
1.子类在覆盖父类方法时,父类的方法如果抛出了异常,那么子类的方法只能抛出父类的异常或者该异常的子类。
2.如果父类抛出多个异常,那么子类只能抛出父类异常的子集。—-子类覆盖父类只能抛出父类异常或者子类或者子集。如果父类的方法没有抛出异常,那么子类覆盖时绝对不能抛,只能try。
常用异常方法:
Error类的常见子类:
Exception类的常见子类:
RuntimeException类的常见的子类:
JAVA集合类汇总
一、集合与数组
数组(可以存储基本数据类型)是用来存现对象的一种容器,但是数组的长度固定,不适合在对象数量未知的情况下使用。
集合(只能存储对象,对象类型可以不一样)的长度可变,可在多数情况下使用。
二、层次关系
Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类。但是却让其被继承产生了两个接口,就是Set和List。
- Set中不能包含重复的元素。
- List是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式。
Map是Java.util包中的另一个接口,它和Collection接口没有关系,是相互独立的,但是都属于集合类的一部分。Map包含了key-value对。Map不能包含重复的key,但是可以包含相同的value。
- Iterator,所有的集合类,都实现了Iterator接口,这是一个用于遍历集合中元素的接口,主要包含以下三种方法:
- hasNext()是否还有下一个元素。
- next()返回下一个元素。
- remove()删除当前元素。
三、几种重要的接口和类简介
- List(有序、可重复)
List里存放的对象是有序的,同时也是可以重复的,List关注的是索引,拥有一系列和索引相关的方法,查询速度快。因为往list集合里插入或删除数据时,会伴随着后面数据的移动,所有插入删除数据速度慢。
- Set(无序、不能重复)
Set里存放的对象是无序,不能重复的,集合中的对象不按特定的方式排序,只是简单地把对象加入集合中。
- Map(键值对、键唯一、值不唯一)
Map集合中存储的是键值对,键不能重复,值可以重复。根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到相应的值。
对比如下:
|
|
是否有序 |
是否允许元素重复 |
Collection |
|
|
|
List |
是 |
是 |
|
Set |
AbstractSet |
否 |
否 |
|
HashSet |
|
|
|
TreeSet |
是(用二叉排序树) |
|
Map |
AbstractMap |
否 |
使用key-value来映射和存储数据,key必须唯一,value可以重复 |
|
HashMap |
|
|
|
TreeMap |
是(用二叉排序树) |
|
四、遍历
在类集中提供了以下四种的常见输出方式:
1)Iterator:迭代输出,是使用最多的输出方式。
2)ListIterator:是Iterator的子接口,专门用于输出List中的内容。
3)foreach输出:JDK1.5之后提供的新功能,可以输出数组或集合。
4)for循环
代码示例如下:
for的形式:
1 2 3
| for(int i=0;i<arr.size();i++){ ... }
|
foreach的形式:
iterator的形式:
1 2 3 4
| Iterator it = arr.iterator(); while(it.hasNext()){ object o =it.next(); ...}
|
五、ArrayList和LinkedList
ArrayList和LinkedList在用法上没有区别,但是在功能上还是有区别的。LinkedList经常用在增删操作较多而查询操作很少的情况下,ArrayList则相反。
六、Map集合
实现类:HashMap、Hashtable、LinkedHashMap和TreeMap
HashMap是最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。因为键对象不可以重复,所以HashMap最多只允许一条记录的键为Null,允许多条记录的值为Null,是非同步的
Hashtable与HashMap类似,是HashMap的线程安全版,它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢,它继承自Dictionary类,不同的是它不允许记录的键或者值为null,同时效率较低。
线程安全,并且锁分离。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hash table,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。
LinkedHashMap保存了记录的插入顺序,在用Iteraor遍历LinkedHashMap时,先得到的记录肯定是先插入的,在遍历的时候会比HashMap慢,有HashMap的全部特性。
TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序(自然顺序),也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。不允许key值为空,非同步的;
map的遍历
第一种:KeySet()
将Map中所有的键存入到set集合中。因为set具备迭代器。所有可以迭代方式取出所有的键,再根据get方法。获取每一个键对应的值。 keySet():迭代后只能通过get()取key 。
取到的结果会乱序,是因为取得数据行主键的时候,使用了HashMap.keySet()方法,而这个方法返回的Set结果,里面的数据是乱序排放的。
典型用法如下:
1 2 3 4 5
| Map map = new HashMap(); map.put("key1","lisi1"); map.put("key2","lisi2"); map.put("key3","lisi3"); map.put("key4","lisi4");
|
//先获取map集合的所有键的set集合,keyset()
1 2 3 4 5 6
| Iterator it = map.keySet().iterator(); while(it.hasNext()){ Object key = it.next(); System.out.println(map.get(key)); }
|
第二种:entrySet()
Set<Map.Entry<K,V>>
entrySet() //返回此映射中包含的映射关系的 Set 视图。(一个关系就是一个键-值对),就是把(key-value)作为一个整体一对一对地存放到Set集合当中的。Map.Entry表示映射关系。entrySet():迭代后可以e.getKey(),e.getValue()两种方法来取key和value。返回的是Entry接口。
典型用法如下:
1 2 3 4 5
| Map map = new HashMap(); map.put("key1","lisi1"); map.put("key2","lisi2"); map.put("key3","lisi3"); map.put("key4","lisi4");
|
//将map集合中的映射关系取出,存入到set集合
1 2 3 4 5
| Iterator it = map.entrySet().iterator(); while(it.hasNext()){ Entry e =(Entry) it.next(); System.out.println("键"+e.getKey () + "的值为" + e.getValue()); }
|
推荐使用第二种方式,即entrySet()方法,效率较高。
对于keySet其实是遍历了2次,一次是转为iterator,一次就是从HashMap中取出key所对于的value。而entryset只是遍历了第一次,它把key和value都放到了entry中,所以快了。两种遍历的遍历时间相差还是很明显的。
七、 List集合
list中添加,获取,删除元素;
list中是否包含某个元素;
list中根据索引将元素数值改变(替换);
list中查看(判断)元素的索引;
根据元素索引位置进行的判断;
利用list中索引位置重新生成一个新的list(截取集合);
对比两个list中的所有元素;
判断list是否为空;
返回Iterator集合对象;
将集合转换为字符串;
将集合转换为数组;
集合类型转换;
去重复;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
| package MyTest01; import java.util.ArrayList; import java.util.List; public class ListTest01 { public static void main(String[] args) { List<String> person=new ArrayList<>(); person.add("jackie"); person.add("peter"); person.add("annie"); person.add("martin"); person.add("marry"); person.remove(3); person.remove("marry"); String per=""; per=person.get(1); System.out.println(per); for (int i = 0; i < person.size(); i++) { System.out.println(person.get(i)); } List<String> fruits=new ArrayList<>(); fruits.add("苹果"); fruits.add("香蕉"); fruits.add("桃子"); for (int i = 0; i < fruits.size(); i++) { System.out.println(fruits.get(i)); } String appleString="苹果"; System.out.println("fruits中是否包含苹果:"+fruits.contains(appleString)); if (fruits.contains(appleString)) { System.out.println("我喜欢吃苹果"); }else { System.out.println("我不开心"); } String a="白龙马", b="沙和尚", c="八戒", d="唐僧", e="悟空"; List<String> people=new ArrayList<>(); people.add(a); people.add(b); people.add(c); people.set(0, d); people.add(1, e); for(String str:people){ System.out.println(str); } List<String> names=new ArrayList<>(); names.add("刘备"); names.add("关羽"); names.add("张飞"); names.add("刘备"); names.add("张飞"); System.out.println(names.indexOf("刘备")); System.out.println(names.lastIndexOf("刘备")); System.out.println(names.indexOf("张飞")); System.out.println(names.lastIndexOf("张飞")); if (names.indexOf("刘备")==0) { System.out.println("刘备在这里"); }else if (names.lastIndexOf("刘备")==3) { System.out.println("刘备在那里"); }else { System.out.println("刘备到底在哪里?"); } List<String> phone=new ArrayList<>(); phone.add("三星"); phone.add("苹果"); phone.add("锤子"); phone.add("华为"); phone.add("小米"); for(String pho:phone){ System.out.println(pho); } phone=phone.subList(1, 4); for (int i = 0; i < phone.size(); i++) { System.out.println("新的list包含的元素是"+phone.get(i)); } if (person.equals(fruits)) { System.out.println("两个list中的所有元素相同"); }else { System.out.println("两个list中的所有元素不一样"); } if (person.hashCode()==fruits.hashCode()) { System.out.println("我们相同"); }else { System.out.println("我们不一样"); } if (person.isEmpty()) { System.out.println("空的"); }else { System.out.println("不是空的"); } System.out.println("返回Iterator集合对象:"+person.iterator()); String liString=""; liString=person.toString(); System.out.println("将集合转换为字符串:"+liString); System.out.println("将集合转换为数组:"+person.toArray()); List<Object> listsStrings=new ArrayList<>(); for (int i = 0; i < person.size(); i++) { listsStrings.add(person.get(i)); } List<StringBuffer> lst=new ArrayList<>(); for(String string:person){ lst.add(StringBuffer(string)); } } private static StringBuffer StringBuffer(String string) { return null; } }
|
八、Set集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
Set<String> set = new HashSet<String>(); Iterator<String> it = set.iterator(); while (it.hasNext()) { String str = it.next(); System.out.println(str); }
for (String str : set) { System.out.println(str); }
Set<Object> set = new HashSet<Object>();
for (Object obj: set) { if(obj instanceof Integer){ int aa= (Integer)obj; }else if(obj instanceof String){ String aa = (String)obj } ........ }
|
九、主要实现类区别小结
Vector和ArrayList
- vector是线程同步的,所以它也是线程安全的,而arraylist是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用arraylist效率比较高。
- 如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而arraylist增长率为目前数组长度的50%。如果在集合中使用数据量比较大的数据,用vector有一定的优势。
- 如果查找一个指定位置的数据,vector和arraylist使用的时间是相同的,如果频繁的访问数据,这个时候使用vector和arraylist都可以。而如果移动一个指定位置会导致后面的元素都发生移动,这个时候就应该考虑到使用linklist,因为它移动一个指定位置的数据时其它元素不移动。
ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要涉及到数组元素移动等内存操作,所以索引数据快,插入数据慢,Vector由于使用了synchronized方法(线程安全)所以性能上比ArrayList要差,LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入数度较快。
arraylist和linkedlist
- ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
- 对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
- 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。 这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。
HashMap与TreeMap
- HashMap通过hashcode对其内容进行快速查找,而TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的)。
- 在Map 中插入、删除和定位元素,HashMap是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。使用HashMap要求添加的键类明确定义了hashCode()和 equals()的实现。
两个map中的元素一样,但顺序不一样,导致hashCode()不一样。
同样做测试:
在HashMap中,同样的值的map,顺序不同,equals时,false;
而在treeMap中,同样的值的map,顺序不同,equals时,true,说明,treeMap在equals()时是整理了顺序了的。
HashTable与HashMap
- 同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的。
- HashMap允许存在一个为null的key,多个为null的value 。
- hashtable的key和value都不允许为null。
Java IO流
一、IO流概述
概述:
IO流简单来说就是Input和Output流,IO流主要是用来处理设备之间的数据传输,java对于数据的操作都是通过流实现,而java用于操作流的对象都在IO包中。
分类:
按操作数据分为:字节流和字符流。 如:Reader和InpurStream
按流向分:输入流和输出流。如:InputStream和OutputStream
IO流常用的基类:
* InputStream , OutputStream
字符流的抽象基类:
* Reader , Writer
由上面四个类派生的子类名称都是以其父类名作为子类的后缀:
如:FileReader和FileInputStream
二、字符流
1. 字符流简介:
* 字符流中的对象融合了编码表,也就是系统默认的编码表。我们的系统一般都是GBK编码。
* 字符流只用来处理文本数据,字节流用来处理媒体数据。
* 数据最常见的表现方式是文件,字符流用于操作文件的子类一般是FileReader和FileWriter。
2.字符流读写:
注意事项:
写入文件后必须要用flush()刷新。
用完流后记得要关闭流
使用流对象要抛出IO异常
定义文件路径时,可以用“/”或者“\”。
在创建一个文件时,如果目录下有同名文件将被覆盖。
在读取文件时,必须保证该文件已存在,否则出异常
示例1:在硬盘上创建一个文件,并写入一些文字数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class FireWriterDemo { public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("F:\\1.txt");
fw.write("hello world!"); fw.flush();
fw.write("first_test"); fw.close();
} }
|
示例2:FileReader的reade()方法.
要求:用单个字符和字符数组进行分别读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| class FileReaderDemo { public static void main(String[] args) { characters(); }
private static void characters() {
try {
FileReader fr = new FileReader("Demo.txt"); char [] buf = new char[6]; int num = 0; while((num = fr.read(buf))!=-1) {
sop(new String(buf,0,num)); } sop('\n'); fr.close(); } catch (IOException e) { sop(e.toString()); } }
private static void singleReader() { try {
FileReader fr = new FileReader("Demo.txt");
int ch = 0; while ((ch=fr.read())!=-1) { sop((char)ch); } sop('\n'); fr.close();
} catch (IOException e) { sop(e.toString()); } }
private static void sop(Object obj) { System.out.print(obj); }
}
|
示例3:对已有文件的数据进行续写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import java.io.*;
class FileWriterDemo3 { public static void main(String[] args) { try { FileWriter fw = new FileWriter("F:\\java_Demo\\day9_24\\demo.txt",true); fw.write(" is charactor table?"); fw.close(); } catch (IOException e) { sop(e.toString()); } }
private static void sop(Object obj) { System.out.println(obj); } }
|
练习:
将F盘的一个文件复制到E盘。
思考:
其实就是将F盘下的文件数据存储到D盘的一个文件中。
步骤:
1.在D盘创建一个文件,存储F盘中文件的数据。
2.定义读取流和F:盘文件关联。
3.通过不断读写完成数据存储。
4.关闭资源。
源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| import java.io.*; import java.util.Scanner;
class CopyText { public static void main(String[] args) throws IOException { sop("请输入要拷贝的文件的路径:"); Scanner in = new Scanner(System.in); String source = in.next(); sop("请输入需要拷贝到那个位置的路径以及生成的文件名:"); String destination = in.next(); in.close(); CopyTextDemo(source,destination);
}
private static void CopyTextDemo(String source,String destination) {
try { FileWriter fw = new FileWriter(destination); FileReader fr = new FileReader(source); char [] buf = new char[1024]; int num = 0; while((num = fr.read(buf))!=-1) { fw.write(new String(buf,0,num)); } fr.close(); fw.close(); } catch (IOException e) { sop(e.toString()); } }
private static void sop(Object obj) { System.out.println(obj); } }
|
三、缓冲区
1. 字符流的缓冲区
BufferedReader和BufferedWreiter
- 缓冲区的出现时为了提高流的操作效率而出现的.
- 需要被提高效率的流作为参数传递给缓冲区的构造函数
- 在缓冲区中封装了一个数组,存入数据后一次取出
BufferedReader示例:
读取流缓冲区提供了一个一次读一行的方法readline,方便对文本数据的获取。
readline()只返回回车符前面的字符,不返回回车符。如果是复制的话,必须加入newLine(),写入回车符
newLine()是java提供的多平台换行符写入方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import java.io.*;
class BufferedReaderDemo { public static void main(String[] args) throws IOException {
FileReader rw = new FileReader("buf.txt");
BufferedReader brw = new BufferedReader(rw);
for(;;) { String s = brw.readLine(); if(s==null) break; System.out.println(s); } brw.close();
} }
|
BufferedWriter示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| import java.io.*;
class BufferedWriterDemo { public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("buf.txt");
BufferedWriter bfw = new BufferedWriter(fw);
for(int x = 1; x < 5; x++) { bfw.write("abc"); bfw.newLine(); bfw.flush(); }
bfw.flush(); bfw.close();
fw.close();
} }
|
2.装饰设计模式
装饰设计模式::::
要求:自定义一些Reader类,读取不同的数据(装饰和继承的区别)
1 2 3 4 5
| class MyBufferReader { MyBufferReader (MyTextReader text) {} MyBufferReader (MyMediaReader media) {} MyBufferReader (MyDataReader data) {} }
|
但是上面的类拓展性很差。找到其参数的共同类型,通过多态的形式,可以提高拓展性
1 2 3 4
| class MyBufferReader extends MyReader{ private MyReader r; MyBufferReader(MyReader r) {} }
|
优化后的体系:
MyTextReader
MyMediaReader
MyDataReader
MyBufferReader //增强上面三个。装饰模式比继承灵活,
避免继承体系的臃肿。降低类与类之间的耦合性
装饰类只能增强已有的对象,具备的功能是相同的。所以装饰类和被装饰类属于同一个体系
MyBuffereReader类: 自己写一个MyBuffereReader类,功能与BuffereReader相同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| class MyBufferedReader1 extends Reader{ private Reader r; MyBufferedReader1(Reader r){ this.r = r; }
public String myReaderline() throws IOException { StringBuilder sb = new StringBuilder(); int ch = 0; while((ch = r.read()) != -1) { if(ch == '\r') continue; if(ch == '\n') return sb.toString(); else sb.append((char)ch); } if(sb.length()!=0) return sb.toString(); return null; }
public void close()throws IOException { r.close(); }
public int read(char[] cbuf,int off, int len)throws IOException { return r.read(cbuf,off,len); }
public void myClose() throws IOException{ r.close(); } }
|
四、字节流
参考: http://blog.csdn.net/qq_28261343/article/details/52678681
Java 多线程
JAVA多线程实现的四种方式
Java多线程实现方式主要有四种:继承Thread类、实现Runnable接口、实现Callable接口通过FutureTask包装器来创建Thread线程、使用ExecutorService、Callable、Future实现有返回结果的多线程。
其中前两种方式线程执行完后都没有返回值,后两种是带返回值的。
继承Thread类创建线程
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。例如:
1 2 3 4 5 6 7 8 9 10
| public class MyThread extends Thread { public void run() { System.out.println("MyThread.run()"); } } MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); myThread1.start(); myThread2.start();
|
实现Runnable接口创建线程
如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个Runnable接口,如下:
1 2 3 4 5
| public class MyThread extends OtherClass implements Runnable { public void run() { System.out.println("MyThread.run()"); } }
|
为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:
1 2 3
| MyThread myThread = new MyThread(); Thread thread = new Thread(myThread); thread.start();
|
事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:
1 2 3 4 5
| public void run() { if (target != null) { target.run(); } }
|
实现Callable接口通过FutureTask包装器来创建Thread线程
Callable接口(也只有一个方法)定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public interface Callable<V> { V call() throws Exception; } public class SomeCallable<V> extends OtherClass implements Callable<V> {
@Override public V call() throws Exception { return null; }
} Callable<V> oneCallable = new SomeCallable<V>();
FutureTask<V> oneTask = new FutureTask<V>(oneCallable);
Thread oneThread = new Thread(oneTask); oneThread.start();
|
使用ExecutorService、Callable、Future实现有返回结果的线程
ExecutorService、Callable、Future三个接口实际上都是属于Executor框架。返回结果的线程是在JDK1.5中引入的新特征,有了这种特征就不需要再为了得到返回值而大费周折了。而且自己实现了也可能漏洞百出。
可返回值的任务必须实现Callable接口。类似的,无返回值的任务必须实现Runnable接口。
执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。
注意:get方法是阻塞的,即:线程无返回结果,get方法会一直等待。
再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了。
下面提供了一个完整的有返回结果的多线程测试例子,在JDK1.5下验证过没问题可以直接使用。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| import java.util.concurrent.*; import java.util.Date; import java.util.List; import java.util.ArrayList; / * 有返回值的线程 */ @SuppressWarnings("unchecked") public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("----程序开始运行----"); Date date1 = new Date(); int taskSize = 5; ExecutorService pool = Executors.newFixedThreadPool(taskSize); List<Future> list = new ArrayList<Future>(); for (int i = 0; i < taskSize; i++) { Callable c = new MyCallable(i + " "); Future f = pool.submit(c); list.add(f); } pool.shutdown(); for (Future f : list) { System.out.println(">>>" + f.get().toString()); } Date date2 = new Date(); System.out.println("----程序结束运行----,程序运行时间【" + (date2.getTime() - date1.getTime()) + "毫秒】"); } } class MyCallable implements Callable<Object> { private String taskNum; MyCallable(String taskNum) { this.taskNum = taskNum; } public Object call() throws Exception { System.out.println(">>>" + taskNum + "任务启动"); Date dateTmp1 = new Date(); Thread.sleep(1000); Date dateTmp2 = new Date(); long time = dateTmp2.getTime() - dateTmp1.getTime(); System.out.println(">>>" + taskNum + "任务终止"); return taskNum + "任务返回运行结果,当前任务时间【" + time + "毫秒】"; } }
|
代码说明:
上述代码中Executors类,提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService接口。
public static ExecutorService newFixedThreadPool(int nThreads)
创建固定数目线程的线程池。
public static ExecutorService newCachedThreadPool()
创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
public static ExecutorService newSingleThreadExecutor()
创建一个单线程化的Executor。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。
ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。
软件-注重思想、逻
Java反射机制详解
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
1、关于Class
1、Class是一个类,一个描述类的类(也就是描述类本身),封装了描述方法的Method,描述字段的Filed,描述构造器的Constructor等属性
2、对象照镜子后(反射)可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。
3、对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。
一个 Class 对象包含了特定某个类的有关信息。
4、Class 对象只能由系统建立对象
5、一个类在 JVM 中只会有一个Class实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package com.java.reflection;
public class Person { String name; private int age;
public Person() { System.out.println("无参构造器"); }
public Person(String name, int age) { System.out.println("有参构造器"); this.name = name; this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
|
2、反射机制获取类有三种方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
@Test public void testGetClass() throws ClassNotFoundException { Class clazz = null;
clazz = Person.class; System.out.println("通过类名: " + clazz);
Object obj = new Person(); clazz = obj.getClass(); System.out.println("通过getClass(): " + clazz);
clazz = Class.forName("com.java.reflection.Person"); System.out.println("通过全类名获取: " + clazz); }
|
- 通过类名: class com.java.reflection.Person
- 无参构造器
- 通过getClass(): class com.java.reflection.Person
- 通过全类名获取: class com.java.reflection.Person
3、利用newInstance创建对象:调用的类必须有无参的构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
@Test public void testNewInstance() throws ClassNotFoundException, IllegalAccessException, InstantiationException { Class clazz = Class.forName("com.java.reflection.Person"); Object obj = clazz.newInstance(); System.out.println(obj); }
|
- 无参构造器
Person{name=’null’, age=0}
- /**
- Class类的newInstance()方法,创建类的一个对象。
- */
- @Test
- public void testNewInstance()
- throws ClassNotFoundException, IllegalAccessException, InstantiationException {
- Class clazz = Class.forName(“com.java.reflection.Person”);
- //使用Class类的newInstance()方法创建类的一个对象
- //实际调用的类的那个 无参数的构造器(这就是为什么写的类的时候,要写一个无参数的构造器,就是给反射用的)
- //一般的,一个类若声明了带参数的构造器,也要声明一个无参数的构造器
- Object obj = clazz.newInstance();
- System.out.println(obj);
- }
4、ClassLoader类加载器
类加载器详解:http://blog.csdn.net/ochangwen/article/details/51473120
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
@Test public void testClassLoader1() throws ClassNotFoundException, IOException { ClassLoader classLoader = ClassLoader.getSystemClassLoader(); System.out.println("系统的类加载器-->" + classLoader);
classLoader = classLoader.getParent(); System.out.println("扩展类加载器-->" + classLoader);
classLoader = classLoader.getParent(); System.out.println("启动类加载器-->" + classLoader);
classLoader = Class.forName("com.java.reflection.Person").getClassLoader(); System.out.println("当前类由哪个类加载器进行加载-->"+classLoader);
classLoader = Class.forName("java.lang.Object").getClassLoader(); System.out.println("JDK提供的Object类由哪个类加载器加载-->" + classLoader); }
|
- 系统的类加载器–>sun.misc.Launcher$AppClassLoader@43be2d65
- 扩展类加载器–>sun.misc.Launcher$ExtClassLoader@7a9664a1
- 启动类加载器–>null
- 当前类由哪个类加载器进行加载–>sun.misc.Launcher$AppClassLoader@43be2d65
- JDK提供的Object类由哪个类加载器加载–>null
getResourceAsStream方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Test public void testGetResourceAsStream() throws ClassNotFoundException, IOException {
InputStream in = this.getClass().getClassLoader() .getResourceAsStream("com/java/reflection/test.properties"); System.out.println("in: " +in); Properties properties = new Properties(); properties.load(in); String driverClass = properties.getProperty("dirver"); String jdbcUrl = properties.getProperty("jdbcUrl"); String user = new String(properties.getProperty("user").getBytes("ISO-8859-1"), "UTF-8"); String password = properties.getProperty("password"); System.out.println("diverClass: "+driverClass); System.out.println("user: " + user); }
|
5、Method: 对应类中的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| public class Person { private String name; private int age;
private void privateMthod(){ } public Person() { System.out.println("无参构造器"); }
public Person(String name, int age) { System.out.println("有参构造器"); this.name = name; this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public void setName(String name , int age){ System.out.println("name: " + name); System.out.println("age:"+ age);
}
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| @Test public void testMethod() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { Class clazz = Class.forName("com.java.reflection.Person");
Method[] methods =clazz.getMethods(); System.out.print(" getMethods: "); for (Method method : methods){ System.out.print(method.getName() + ", "); }
Method[] methods2 = clazz.getDeclaredMethods(); System.out.print("\ngetDeclaredMethods: "); for (Method method : methods2){ System.out.print(method.getName() + ", "); }
Method method = clazz.getDeclaredMethod("setName",String.class); System.out.println("\nmethod : " + method);
Method method2 = clazz.getDeclaredMethod("setName",String.class ,int.class); System.out.println("method2: " + method2);
Object obj = clazz.newInstance(); method2.invoke(obj, "changwen", 22); }
|
getMethods: toString, getName, setName, setName, setAge, getAge, wait, wait, wait, equals, hashCode, getClass, notify, notifyAll,
getDeclaredMethods: toString, getName, setName, setName, setAge, getAge, privateMthod,
method : public void com.java.reflection.Person.setName(java.lang.String)
method2: public void com.java.reflection.Person.setName(java.lang.String,int)
无参构造器
name: changwen
age:22