集合
集合框架
-
Collection接口:单列数据,定义了存取一个对象的方法的集合
- List:元素有序,可重复的集合 动态数组
ArrayList LinkedList Vector
都实现了List接口,都可以存储有序的可重复的数据ArrayList
: 作为List接口的主要实现类,线程不安全,效率高,底层使用Object[]存储- 建议开发使用带参构造器,规定大小
List list = new ArrayList(10);
- 建议开发使用带参构造器,规定大小
LinkedList
: 对于频繁插入,删除操作,效率比ArrayList
高 , 底层使用双向链表存储Vector
: List接口古老实现类,线程安全,效率低,底层使用Object[]存储
- Set:元素无序,不可重复的集合
- 没有额外的定义新的方法,使用Collection中声明的方法
- 无序性: 不等于随机性 . 以
HashSet
为例 , 存储的数据在底层数组中并非按照数组索引的顺序添加 , 而是根据数据的哈希值添加 - 不可重复性: 保证添加的元素按照equals()判断时,不能返回true , 即相同元素只能添加一个
HashSet
Set接口主要实现类,线程不安全,可以存储null 通过HashSet实现LinkedHashSet
作为HashSet
子类,使得遍历其内部数据时,可以按照添加的顺序遍历 增加头尾指针,方便遍历TreeSet
可以按照添加对象的指定属性排序
- List:元素有序,可重复的集合 动态数组
-
Map接口:双列数据,保存具有映射的关系的键值对
HashMap
作为Map的主要实现类 线程不安全 , 效率高 可以存储null的key和valueLinkedHashMap
保证在遍历map元素时,可以按照添加顺序实现遍历,频繁遍历时,效率高
TreeMap
保证按照key-value对排序,实现对key的自然排序和定制排序 底层使用红黑树Hashtable
作为Map的古老实现类 线程安全 , 效率低 不可以存储null的key和valueProperties
常用来处理配置文件,key和value都是String类型
Collection自定义对象重写方法
-
List:equals()方法
-
Set:
- (HashSet , LinkedHashSet):equals() , hashCode()
- TreeSet
- Comparable:comapreTo(Object obj)
- Comparator:compare(Object obj1,Object obj2)
-
Map
Collection 接口方法
Collection col=new ArrayList();
//add(Object e) 将元素e添加到集合col中
col.add("AA");
col.add("BB");
col.add(1234);//自动装箱
col.add(new Date());
//size() 获取添加的元素的个数
System.out.println(col.size());//4
//addAll():
Collection col1=new ArrayList();
col1.add(456);
col1.add(4.5f);
col1.add("EEEE");
col.addAll(col1);
System.out.println(col.size());//7
System.out.println(col);//toString [AA, BB, 1234, Fri Jul 29 10:08:45 HKT 2022, 456, 4.5, EEEE]
//clear() 清空集合元素
col.clear();
//isEmpty() 判断结合是否为空
System.out.println(col.isEmpty());
Collection col1=new ArrayList();
col1.add(123);
col1.add(4.55f);
col1.add(123);
col1.add(123);
col1.add(123);
col1.add(new String("tom"));
col1.add(false);//Boolean型
col1.add(456);
col1.add(new Person2("hhh",25));
//contains(Object obj) 判断当前集合是否包含obj,通过调用obj对象所在类的equals方法实现,最好重写equal方法
System.out.println(col1.contains(123));//true
System.out.println(col1.contains(new String("tom")));//true 比较值,调用equals
System.out.println(col1.contains(new Person2("hhh",25)));//不重写equals则为false 重写equals比较值为true
//containsAll(Object obj)判断obj中的所有元素是否都存在于当前集合中
Collection col2= Arrays.asList(123,456);
System.out.println(col1.containsAll(col2));
//remove(object obj)从当前元素中移除一个元素 调用obj对象所在类的equals方法实现 返回值boolean 表示是否移除成功
col1.remove(123);
System.out.println(col1);
//remove(Collection coll)从当前元素中移除对应的所有元素(包括重复的) 调用对象所在类的equals方法实现 返回值boolean 表示是否移除成功
col1.removeAll(col2);
System.out.println(col1);
//boolean retainAll(Collection c) 获取两个集合交集 把交集的结果存在当前集合中,不影响c
col1.retainAll(col2);
System.out.println(col1);//[123, 123, 123, 123, 456]
System.out.println(col2);//[123, 456]
//equals(Object obj) 集合是否相等 要想返回true,需要当前集合和形参集合的元素都相同
Collection col3=new ArrayList();
col3.addAll(col1);
System.out.println(col1.equals(col3));
//hashCode() 返回当前对象哈希值
System.out.println(col1.hashCode());
//toArray 集合---->数组
Object []n=col1.toArray();
System.out.println(Arrays.toString(n));
//Arrays.asList 数组---->集合 注意:使用基本数据类型如int可能会把数组所有元素当成集合的一个元素,如果是包装类如果Interger则不会
List<String> list =Arrays.asList(new String[]{"AA","BB","CC"});
System.out.println(list);
迭代器
iterator():返回迭代器对象,用于集合遍历,不用来遍历map
//iterator()方法
//iterator():返回迭代器对象,用于集合遍历,使用迭代器接口Iterator
Iterator iterator= col1.iterator();
while (iterator.hasNext()){//hasNext() 检验下一个数据是否存在
System.out.println(iterator.next());//iterator.next() 指向下一个数据
}
Iterator iter = col1.iterator();//回到起点
while(iter.hasNext()){
Object obj = iter.next();
if(obj.equals(123)){
iter.remove();//remove() 删除集合中的元素,调用的为迭代器的remove方法
}
}
System.out.println(col1);
foreach
JDK5.0新增,用于遍历集合,数组,赋值给局部变量,修改局部变量值,原值不变
for(Object obj:col1)//(集合的元素类型 局部变量 :集合对象)
System.out.println(obj);
@Test
public void test4(){
int []arr=new int[]{1,2,3,44,5,6};
for(int i:arr)
System.out.println(i);
}
List常用方法
List list = new ArrayList(10);
System.out.println(list.size());
list.add(1223);
list.add(3);
list.add(6.456f);
list.add(false);
list.add(new String("dd"));
System.out.println(list);
//void add(int index,Object obj)在index处插入obj元素
list.add(1,new Person2("hhh",25));
System.out.println(list);
//boolean addAll(int index,Object obj)在index处插入obj的所有元素
List list1 = Arrays.asList(1, 2, 3, 4, 5);
list.addAll(1,list1);
System.out.println(list);
//Object get(int index)获取index位置的元素
System.out.println(list.get(6));
//int indexOf(Object obj):返回obj元素在集合第一次出现的位置,没有返回-1
System.out.println(list.indexOf(3));
//int LastIndexOf(Object obj):返回obj元素在集合最后一次出现的位置,没有返回-1
System.out.println(list.lastIndexOf(3));
//Object remove(int index):移除index位置的对象,并返回此元素 注意与remove(Collection coll)区别 list.remove(new Interger(i))可实现remove指定对象
System.out.println(list.remove(5));
System.out.println(list);
//Object set(int index,Object obj)设置指定index位置的元素为obj
list.set(1,114514);
System.out.println(list);
//List subList(int fromIndex,int toIndex)返回从fromIndex到toIndex的子集合,左闭右开区间
System.out.println(list.subList(1,4));//[1-4)
//遍历 foreach iterator fori(list.get(i))
//LinkedList
void addFirst(Object obj)
void addLast(Object obj)
Object getFirst()
Object getLast()
Object removeFirst()
Object removeLast()
//Vector
void addElement(Object obj)
void insertElementAt(Object obj,int index)
void setElementAt(Object obj,int index)
void removeElement(Object obj)
void removeAllElements()
Set
添加元素的过程 , 以HashSet
为例:
-
添加元素a,首先调用a所在类的hashCode()方法,计算a的哈希值
-
remove()操作时计算哈希值,故而,如果修改了对象值,可能会删除元素失败,甚至误删其他元素
-
通过某种算法,计算出在
HashSet
底层数组中存放位置,即索引位置,判断数组此位置上是否已经存在元素- 若无元素,添加成功,存储在初始的数组中,返回true
- 若此位置存在其他元素b或以链表形式存在的多个元素,则比较元素a与元素b的hash值
- 如果hash值不相同,则元素a添加成功,以链表方式存储
- 如果hash值相同,调用a所在类的equals()方法
- **返回true,**元素相同,添加失败
- 返回false,元素不同,添加成功,以链表方式存储
jdk7中,元素a放到数组中,指向原来的元素
jdk8中 原来的元素在数组中,指向a
重写equals() hashCode()
- 向Set中添加数据,其所在的类一定要重写
equals() hashCode()
- 先计算hashCode()后计算equals
- 重写的两个方法尽可能保持一致性,相等的对象必须有相同的散列码
- 在程序运行时,同一个对象多次调用hashCode()应该返回相同的值
- 当两个对象的equals()比较返回true时,hashCode()返回值也应该相等
//可以自定义,要确保属性值相同时,hashCode相同
@Override
public int hashCode() {
int result;
result = (this.name!=null)?this.name.hashCode():0;7
result=31*result+this.age;
return result;
}
@Override
public boolean equals(Object o){
System.out.println("Override equals");
if(this==o)
return true;
if(o==null||getClass()!=o.getClass())
return false;
Person2 person=(Person2) o;
return (age==person.age) && (Objects.equals(name,person.name));
}
TreeSet
- 向TreeSet类添加元素,要求是相同类的对象
- 添加对象从小到大排序
- 比较对象实现两种排序方式:自然排序(实现Comparable接口) , 定制排序(Comparator)
- 自然排序中判断对象是否相同按照
compareTo()
是否返回0 , 不再是equals方法 , 不比较全部数据容易丢失属性 - 定制排序中判断对象是否相同按照
compare()
是否返回0 , 不再是equals方法 , 不比较全部数据容易丢失属性
Set remove()
remove()操作时计算哈希值,故而,如果修改了对象值,可能会删除元素失败,甚至误删其他元素
HashSet set=new HashSet();
Person2 p1=new Person2("AA",111);
Person2 p2=new Person2("BB",222);
set.add(p1);
set.add(p2);
System.out.println(set);
p1.setName("CC");
set.remove(p1);//删除失败,p1最新的哈希值对应的位置没有元素,存储p1的位置仍为原哈希值计算出的位置
System.out.println(set);
set.add(new Person2("CC",111));//位置为当前数据计算出哈希值的位置,与p1不同,插入成功
System.out.println(set);
set.add(new Person2("AA",111));//插入成功
System.out.println(set);
Map
Map结构:
key无序 不可重复 类似Set key所在的类要重写equals() hashCode() (以HashMap为例)
value无序 可以重复 类似Collection value所在的类要重写equals()
一个键值对:key-value 构成一个Entry 无序不可重复 类似Set
HashMap的底层实现原理
JDK7:
-
实例化后,底层创建了长度为16的一维数组Entry[] table
-
…可能已经多次执行put…
-
map.put(key1,value1):
-
调用key1所在类的hashCode()计算key1哈希值,计算后得到Entry数组的存放位置
-
如果位置上数据为空,key1-vaule1添加成功 —情况1
-
如果此位置上数据不为空(此位置存在赢或多个数据(以链表形式存在)),比较key1和已经存在的多个数据的哈希值
- 如果key1与多个存在数据hash值不同,key1-vaule1添加成功 —情况2
- key1和已经存在的某一个数据的哈希值相同,继续比较: 调用key1所在类的equals() 比较:
- 返回false :key1-vaule1添加成功 —情况3
- 返回true :将value1替换相同key的value值
-
-
在不断的添加过程中,会涉及到扩容问题,默认扩容为原来容量的2倍,并将原来的数据复制过来
情况2,情况3:此时key1-value1和原来数据以链表形式存储
JDK8:
- new HashMap():底层没有创建一个长度为16的数组
- 底层数组为Node(),而非Entry[]
- 首次调用put()方法时,底层创建长度为16的数组
- JDK7底层结构为数组+链表 , JDK8底层结构为数组+链表+红黑树
- 当数组的某一个索引位置上的元素以链表形式存在的数据个数>8且当前数组的长度>64时,此时索引位置上的所有数据改为使用红黑树存储
DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
MAXIMUM_CAPACITY : HashMap的最大支持容量,2^30
DEFAULT_LOAD_FACTOR:HashMap的默认加载因子 0.75
TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树
UNTREEIFY_THRESHOLD:Bucket中红黑树存储的Node小于该默认值,转化为链表
MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量。(当桶中Node的 数量大到需要变红黑树时,若hash表容量小于MIN_TREEIFY_CAPACITY时,此时应执行 resize扩容操作这个MIN_TREEIFY_CAPACITY的值至少是TREEIFY_THRESHOLD的4 倍。)
table:存储元素的数组,总是2的n次幂
entrySet:存储具体元素的集
size:HashMap中存储的键值对的数量
modCount:HashMap扩容和结构改变的次数。
threshold:扩容的临界值,=容量*填充因子 12
loadFactor:填充因子
负载因子的大小决定了HashMap的数据密度
负载因子越大密度越大,发生碰撞的几率越高,数组中的链表越容易长, 造成查询或插入时的比较次数增多,性能会下降。
负载因子越小,就越容易触发扩容,数据密度也越小,意味着发生碰撞的 几率越小,数组中的链表也就越短,查询和插入时比较的次数也越小,性 能会更高。但是会浪费一定的内容空间。而且经常扩容也会影响性能,建 议初始化预设大一点的空间。
Map常用方法
//添加、删除、修改操作:
Map map=new HashMap();
Map map1=new HashMap();
//Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
map.put("AAA",123);//添加
map.put("AAA",456);//修改 第一个AAA value被更新成456
map.put("BB",123);
map.put("CCC",123);
map.put("AA",123);
//void putAll(Map m):将m中的所有key-value对存放到当前map中
map1.putAll(map);
System.out.println(map);
System.out.println(map1);
//Object remove(Object key):移除指定key的key-value对,并返回value
System.out.println(map1.remove("AAA"));//输出移除元素的value Object value=map1.remove("AAA");
System.out.println(map1);
//void clear():清空当前map中的所有数据
map1.clear();
System.out.println(map1);
System.out.println(map1.size());//为0,不为null
//元素查询的操作:
//Object get(Object key):获取指定key对应的value
System.out.println(map.get("AAA"));
//boolean containsKey(Object key):是否包含指定的key
System.out.println(map.containsKey("AAA"));
//boolean containsValue(Object value):是否包含指定的value
System.out.println(map.containsValue(123));
//int size():返回map中key-value对的个数
System.out.println(map.size());
//boolean isEmpty():判断当前map是否为空
System.out.println(map1.isEmpty());
//boolean equals(Object obj):判断当前map和参数对象obj是否相等
System.out.println(map.equals(map1));
//元视图操作的方法:
//Set keySet():返回所有key构成的Set集合,从而遍历map
Set set = map.keySet();
Iterator iter= set.iterator();
while (iter.hasNext()){
System.out.println(iter.next());
}
//Collection values():返回所有value构成的Collection集合 遍历顺序与keySet()相同
Collection values = map.values();
for(Object obj :values)
{
System.out.println(obj);
}
/* 任务一:显示键集*/
System.out.println(countries.keySet());
/* 任务二:显示值集*/
System.out.println(countries.values());
/* 任务三:显示键值对集*/
System.out.println(countries);
//方式一: Set entrySet():返回所有key-value对构成的Set集合
Set set1 = map.entrySet();
//System.out.println(set1);
Iterator iter1=set1.iterator();
while (iter1.hasNext()){
Object obj=iter1.next();
Map.Entry entry=(Map.Entry) obj;//entrySet集合中的元素都是entry
System.out.println(entry.getKey()+"----->"+entry.getValue());//getKey(),getValue()
}
//方式二
set = map.keySet();
iter= set.iterator();
while (iter.hasNext()){
Object key=iter.next();
Object value=map.get(key);
System.out.println(key+"----->"+value);
}
for (Object entry: dogMap.entrySet()) {
Map.Entry en=(Map.Entry) entry;
Dog dog=(Dog) en.getValue();
System.out.println(dog.getName()+"\t"+ dog.getStrain());
}
TreeMap
- 向TreeMap中添加key-value , 要求key必须为同一个类创建的对象(需要根据key排序)
@Test
public void test10(){
TreeMap mao=new TreeMap();
mao.put(new Person2("aaa",1234),1);
mao.put(new Person2("BBB",1234),2);
mao.put(new Person2("AA",1234),2);
mao.put(new Person2("aaa",123),3);
mao.put(new Person2("aaa",12345),1);
System.out.println(mao);
}
//实现Comparable接口,重写compareTo
@Override
public int compareTo(Object o) {
Person2 p2=(Person2) o;
int a=this.name.compareTo(p2.getName());
if(a!=0)
return a;
else if (a==0)
return this.age-p2.age;
else
throw new RuntimeException ("输入数据异常");
}
//实现Comparator
@Test
public void test11() {
TreeMap mao=new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Person2&&o2 instanceof Person2){
Person2 p1=(Person2) o1;
Person2 p2=(Person2) o2;
System.out.println("****");
return Integer.compare(p1.getAge(),p2.getAge());
}
throw new RuntimeException("ERROR");
}
});
mao.put(new Person2("aaa",1234),1);
mao.put(new Person2("BBB",1234),2);
mao.put(new Person2("AA",1234),2);
mao.put(new Person2("aaa",123),3);
mao.put(new Person2("aaa",12345),1);
System.out.println(mao);
}
Properties
- Properties 类是 Hashtable 的子类,该对象用于处理属性文件
- 由于属性文件里的 key、value 都是字符串类型,所以 Properties 里的 key 和 value 都是字符串类型
- 存取数据时,建议使用setProperty(String key,String value)方法和 getProperty(String key)方法
- void load(InputStream inStream) 从输入流中读取属性列表 (键和元素对)。 通过对指定文件进行装载获取该文件中所有键 -值对
- void clear() 清除所装载的键-值对,该方法由基类 Hashtable提供
public static void main(String[] args){
FileInputStream fis= null;
try {
Properties pros=new Properties();
fis = new FileInputStream("src\\jdbc.properties");
pros.load(fis);//加载配置文件
String name=pros.getProperty("name");
String password=pros.getProperty("password");
System.out.println("name= "+name+". password= "+password);
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Ctrl+Alt+T选择环绕方式
Collections工具类
- Collections 是一个操作 Set、List 和 Map 等集合的工具类
- Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作, 还提供了对集合对象设置不可变、对集合对象实现同步控制等方法
List list=new ArrayList();
list.add(1);
list.add(3);
list.add(2);
list.add(6);
list.add(5);
list.add(4);
list.add(1);
list.add(4);
System.out.println(list);
//排序操作:(均为static方法)
//reverse(List):反转 List 中元素的顺序
Collections.reverse(list);
System.out.println(list);
//shuffle(List):对 List 集合元素进行随机排序
Collections.shuffle(list);
System.out.println(list);
//sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
Collections.shuffle(list);
System.out.println(list);
//sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
Collections.sort(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Integer i1=(Integer) o1;
Integer i2=(Integer) o2;
return i2-i1;
}
});
System.out.println(list);
//swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
Collections.swap(list,2,4);
System.out.println(list);
//查找、替换
//Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
System.out.println(Collections.max(list));
//Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素,最右边是元素
//Object min(Collection)
System.out.println(Collections.min(list));
//Object min(Collection,Comparator)根据 Comparator 指定的顺序,返回给定集合中的最小元素,最左边是元素
//int frequency(Collection,Object):返回指定集合中指定元素的出现次数
System.out.println(Collections.frequency(list,1));
//void copy(List dest,List src):将src中的内容复制到dest中
List desk= Arrays.asList(new Object[list.size()]);//需要先给定一定的空间
Collections.copy(desk,list);
System.out.println(desk);
//boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List 对象的所有旧值
Collections.replaceAll(list,1,114514);
System.out.println(list);
同步控制方法
创建线程安全的集合
List list1= Collections.synchronizedList(list);//返回一个线程安全的list
Collections.synchronizedCollection()
Collections.synchronizedList()
Collections.synchronizedSet()
Collections.synchronizedMap()
Stack
栈是Vector的一个子类
推荐Deque,因为java的Stack是线程安全的,效率较低
Deque<Integer> stack1,stack2;
public CQueue() {
stack1=new ArrayDeque<>() {
};
stack2=new ArrayDeque<>();
}
序号 | 方法描述 |
---|---|
1 | boolean empty() 测试堆栈是否为空。 |
2 | Object peek( ) 查看堆栈顶部的对象,但不从堆栈中移除它。 |
3 | Object pop( ) 移除堆栈顶部的对象,并作为此函数的值返回该对象。 |
4 | Object push(Object element) 把项压入堆栈顶部。 |
5 | int search(Object element) 返回对象在堆栈中的位置,以 1 为基数。 |
6 | size() |
LinkedList
Queue<TreeNode> q = new LinkedList<TreeNode>();
q.offer(u);//添加元素
q.offer(v);
while (!q.isEmpty()) {
u = q.poll();//从队首获取元素,同时获取的这个元素将从原队列删除
add 增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常
remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
offer 添加一个元素并返回true 如果队列已满,则返回false
poll 移除并返问队列头部的元素 如果队列为空,则返回null
peek 返回队列头部的元素 如果队列为空,则返回null
put 添加一个元素 如果队列满,则阻塞
take 移除并返回队列头部的元素 如果队列为空,则阻塞
方法 | 描述 |
---|---|
public boolean add(E e) | 链表末尾添加元素,返回是否成功,成功为 true,失败为 false。 |
public void add(int index, E element) | 向指定位置插入元素。 |
public boolean addAll(Collection c) | 将一个集合的所有元素添加到链表后面,返回是否成功,成功为 true,失败为 false。 |
public boolean addAll(int index, Collection c) | 将一个集合的所有元素添加到链表的指定位置后面,返回是否成功,成功为 true,失败为 false。 |
public void addFirst(E e) | 元素添加到头部。 |
public void addLast(E e) | 元素添加到尾部。 |
public boolean offer(E e) | 向链表末尾添加元素,返回是否成功,成功为 true,失败为 false。 |
public boolean offerFirst(E e) | 头部插入元素,返回是否成功,成功为 true,失败为 false。 |
public boolean offerLast(E e) | 尾部插入元素,返回是否成功,成功为 true,失败为 false。 |
public void clear() | 清空链表。 |
public E removeFirst() | 删除并返回第一个元素。 |
public E removeLast() | 删除并返回最后一个元素。 |
public boolean remove(Object o) | 删除某一元素,返回是否成功,成功为 true,失败为 false。 |
public E remove(int index) | 删除指定位置的元素。 |
public E poll() | 删除并返回第一个元素。 |
public E remove() | 删除并返回第一个元素。 |
public boolean contains(Object o) | 判断是否含有某一元素。 |
public E get(int index) | 返回指定位置的元素。 |
public E getFirst() | 返回第一个元素。 |
public E getLast() | 返回最后一个元素。 |
public int indexOf(Object o) | 查找指定元素从前往后第一次出现的索引。 |
public int lastIndexOf(Object o) | 查找指定元素最后一次出现的索引。 |
public E peek() | 返回第一个元素。 |
public E element() | 返回第一个元素。 |
public E peekFirst() | 返回头部元素。 |
public E peekLast() | 返回尾部元素。 |
public E set(int index, E element) | 设置指定位置的元素。 |
public Object clone() | 克隆该列表。 |
public Iterator descendingIterator() | 返回倒序迭代器。 |
public int size() | 返回链表元素个数。 |
public ListIterator listIterator(int index) | 返回从指定位置开始到末尾的迭代器。 |
public Object[] toArray() | 返回一个由链表元素组成的数组。 |
public T[] toArray(T[] a) | 返回一个由链表元素转换类型而成的数组。 |
泛型
- 集合接口或集合类JDK5.0时都修改为带泛型的结构
- 在实例化集合类时,可以指明具体的泛型类型
- 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构使用到类的泛型的位置,都指定为实例化的泛型类型
- 泛型的类型必须是类,不能是基本数据类型,使用包装类替代基本数据类型
实例化
ArrayList<Integer> list=new ArrayList<Integer>();
//jdk7后 ArrayList<Integer> list=new ArrayList<>();类型推断
list.add(78);
list.add(55);
list.add(100);
list.add(95);
for(Integer i:list){
System.out.println(i);
}
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
HashMap<Integer,String> map=new HashMap<Integer,String>();
map.put(123,"asc");
map.put(456,"dfs");
Set<Map.Entry<Integer, String>> entries = map.entrySet();
Iterator<Map.Entry<Integer, String>> iterator1 = entries.iterator();
while (iterator1.hasNext()){
Map.Entry<Integer, String> en=iterator1.next();
String value= en.getValue();
Integer key= en.getKey();
System.out.println(key+"--->"+value);
}
比较器
public class Person2 implements Comparable<Person2>{
@Override
public int compareTo(Person2 o) {
int a=this.name.compareTo(o.getName());
if(a!=0)
return a;
else if (a==0)
return this.age-o.age;
else
throw new RuntimeException ("输入数据异常");
}
}
TreeMap<Person2, Object> map=new TreeMap<>(new Comparator<Person2>() {
@Override
public int compare(Person2 o1, Person2 o2) {
System.out.println("****");
return Integer.compare(o1.getAge(),o2.getAge());
}
});
自定义泛型结构
- 泛型可能带有多个参数<E1,E2,E3>
- 类的构造器无需加泛型
- 泛型不同的引用不能相互赋值
- 泛型不指定,对应的类型按照Object类型处理,但是不等于Object类型
- 静态方法中不能使用泛型(静态结构早于对象创建)
- 异常类不能用泛型
Order order=new Order();//如果定义了泛型类,实例化时没有指明泛型类类型,默认为Object型
order.setOrderT(123);
order.setOrderT("abc");
Order<String> order1=new Order<String>("oreder",1111,"order:String");
//子类在继承带泛型的父类时,指明了泛型类型,声明时无需指明
Suborder suborder=new Suborder();
suborder.setOrderT(111);
Suborder1<String> sub2=new Suborder1<>();
public class Suborder extends Order<Integer>{//不是泛型类
}
public class Suborder1<T> extends Order<T>{//仍然是泛型类
}
public class Order<T> {
String orderName;
int orderId;
//类的内部结构可以使用类的泛型
T orderT;
public Order(){
}
public Order(String orderName,int orderId,T orderT){
this.orderName=orderName;
this.orderId=orderId;
this.orderT=orderT;
}
}
T []arr=(T[]) new Object[10];
子类继承父类泛型
- 不保留
- 没有类型 擦除
- 指定类型
- 保留
- 全部保留
- 部分保留
class Father<T1,T2>{}
class Son1 extends Father{}//等价于class Son1 extends Father<Object,Object>{}
class Son2 extends Father<Integer,String>{}
class Son3 extends Father<T1,T2>{}
class Son4 extends Father<T1,String>{}
List<String> List<Object>
不具有子父类关系,并列关系
Object obj=null;
String str=null;
obj=str;
Object[]arr1=null;
String[]arr2=null;
arr1=arr2;
List<Object> list1=null;
List<String> list2=null;
//list1=list2; 报错,不具备父子类关系,为并列关系
泛型方法
- 在方法中出现了泛型的结构,泛型参数与类的泛型参数没有关系
- 泛型方法与所属的类是不是泛型类没有关系
- 泛型方法调用时,指明泛型参数的类型
- 泛型方法可以声明为static
- [访问权限] <泛型> 返回类型 方法名([泛型标识 参数名称]) 抛出的异常
public <E> List<E> copyFromArraytoList(E[]arr){//E并非类的泛型 <E> List<E>前面声明一个 <E>以区别E是否是类
ArrayList<E> list=new ArrayList<>();
for(E e:arr){
list.add(e);
}
return list;
}
public static <E> List<E> copyFromArraytoList(E[]arr){//E并非类的泛型 <E> List<E>前面声明一个 <E>以区别E是否是类
ArrayList<E> list=new ArrayList<>();
for(E e:arr){
list.add(e);
}
return list;
}
Order<String> order=new Order<>();
Integer []arr=new Integer[]{1,2,3,4,5};
List<Integer> list=order.copyFromArraytoList(arr);//泛型方法调用时,指明泛型参数的类型
System.out.println(list);
public class DAO<T> {//date(base) access object 表的共性操作DAO
//添加记录
public void add(T t) { }
//删除记录
public boolean remove(int index) {
return false;
}
//修改记录
public void update(int index,T t){ }
//查询记录
public T getIndex(int index){
return null;
}
public <E> E getValue(){
return null;
}
}
public class Customerdao extends DAO<Customer>{//只能操作某一个表的DAO
}
Customerdao dao1=new Customerdao();
dao1.add(new Customer());
List<Customer> list=dao1.getForlist(10);
通配符
-
通配符:?
-
G<A> G<B>
没有关系 ,二者共同父类是:G<?> -
对于List<?>不能向内部添加数据,除了添加null
-
List<?>可以向内部读数据
@Test
public void test1(){
List<Object> list1=null;
List<String> list2=null;
List<?> list =null;
list=list1;
list=list2;
}
public void print(List<?> list){
Iterator<?> iterator= list.iterator();
while (iterator.hasNext()){
Object obj=iterator.next();
System.out.println(obj);
}
}
List<String> list3=new ArrayList<>();
list3.add("AA");
list3.add("BB");
list3.add("CC");
System.out.println(list3.size());
list=list3;
list.add(null);
System.out.println(list.size());
Object obj=list.get(0)
System.out.println(obj);
//对于List<?>不能向内部添加数据,除了添加null
有限制条件的通配符
- extends Number> (无穷小 , Number]只允许泛型为Number及Number子类的引用调用
- super Number> [Number , 无穷大) 只允许泛型为Number及Number父类的引用调用
- extends Comparable> 只允许泛型为实现Comparable接口的实现类的引用调用
public static void printCollection3(Collection<? extends Person> coll) {
//Iterator只能用Iterator<?>或Iterator<? extends Person>
Iterator<?> iterator = coll.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
public static void printCollection4(Collection<? super Person> coll) {
//Iterator只能用Iterator<?>或Iterator<? super Person>
Iterator<?> iterator = coll.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
IO
File类
- java.io.File类:文件和文件目录路径的抽象表示形式,与平台无关
- File类的一个对象,代表一个文件或一个文件目录
- File能新建、删除、重命名文件,修改时间,文件大小和目录,但 File 不能访问文件内容本身。 如果需要访问文件内容本身,则需要使用输入/输出流。
- 想要在Java程序中表示一个真实存在的文件或目录,那么必须有一个File对 象,但是Java程序中的一个File对象,可能没有一个真实存在的文件或目录。
- File对象可以作为参数传递给流的构造器
//public File(String pathname)以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。
File f1=new File("hello.txt");//当对于当前module的
File f2=new File("D:\\javalearn\\he.txt");
File f3 = new File("d:" + File.separator + "javalearn" + File.separator + "info.txt");
System.out.println(f1);//hello.txt
System.out.println(f2);//D:\javalearn\he.txt
System.out.println(f3);//d:\javalearn\info.txt
//public File(String parent,String child)以parent为父路径,child为子路径创建File对象。
File f4=new File("D:\\javalearn","javaee");
System.out.println(f4);//D:\javalearn\javaee
//public File(File parent,String child)根据一个父File对象和子文件路径创建File对象
File f5=new File(f4,"hi.txt");
System.out.println(f5);//D:\javalearn\javaee\hi.txt
- windows和DOS系统默认使用“\”来表示
- UNIX和URL使用“/”来表示
- public static final String separator。根据操作系统,动态的提供分隔符
File f3 = new File("d:" + File.separator + "javalearn" + File.separator + "info.txt");
File 类的使用:常用方法
//File类的获取功能
public String getAbsolutePath():获取绝对路径
public String getPath() :获取路径
public String getName() :获取名称
public String getParent():获取上层文件目录路径。若无,返回null
public long length() :获取文件长度(即:字节数)。不能获取目录的长度。
public long lastModified() :获取最后一次的修改时间,毫秒值
public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组,适用于文件目录
public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组,适用于文件目录 绝对路径格式
File f1=new File("hello.txt");
File f2=new File("D:\\javalearn\\he.txt");
System.out.println(f1.getAbsolutePath());//D:\javalearn\hello.txt
System.out.println(f1.getPath());//hello.txt
System.out.println(f1.getName());//hello.txt
System.out.println(f1.getParent());//null
System.out.println(f1.length());//0
System.out.println(f1.lastModified());//1659425217042
System.out.println(new Date(f1.lastModified()));//Tue Aug 02 15:26:57 HKT 2022
System.out.println();
System.out.println(f2.getAbsolutePath());//D:\javalearn\he.txt
System.out.println(f2.getPath());//D:\javalearn\he.txt
System.out.println(f2.getName());//he.txt
System.out.println(f2.getParent());//D:\javalearn
System.out.println(f2.length());//0
System.out.println(f2.lastModified());//0
File file=new File("D:\\javalearn");
String[] list = file.list();
for (String a:list){
System.out.println(a);
}
File[] files = file.listFiles();
for(File f:files){
System.out.println(f);
}
//File类的重命名功能
public boolean renameTo(File dest):把文件重命名为指定的文件路径
File f1=new File("hello.txt");
File f2=new File("D:\\javalearn\\hi.txt");
boolean renameTo=f1.renameTo(f2);//要想保证返回true,file1在硬盘中应该存在,file2不应该存在
System.out.println(renameTo);
//File类的判断功能
public boolean isDirectory():判断是否是文件目录
public boolean isFile() :判断是否是文件
public boolean exists() :判断是否存在
public boolean canRead() :判断是否可读
public boolean canWrite() :判断是否可写
public boolean isHidden() :判断是否隐藏
File f1=new File("hi.txt");
System.out.println(f1.isDirectory());
System.out.println(f1.isFile());
System.out.println(f1.exists());
System.out.println(f1.canRead());
System.out.println(f1.canWrite());
System.out.println(f1.isHidden());
f1=new File("D:\\javalearn");
System.out.println(f1.isDirectory());
System.out.println(f1.isFile());
System.out.println(f1.exists());
System.out.println(f1.canRead());
System.out.println(f1.canWrite());
System.out.println(f1.isHidden());
File类的创建功能
- public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false
- public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。 如果此文件目录的上层目录不存在,也不创建。
- public boolean mkdirs() :创建文件目录。如果上层文件目录不存在,一并创建
- 注意事项:如果你创建文件或者文件目录没有写盘符路径,那么,默认在项目路径下。
File类的删除功能
- public boolean delete():删除文件或者文件夹
- 删除注意事项: Java中的删除不走回收站。 要删除一个文件目录,请注意该文件目录内不能包含文件或者文件目录
File f1=new File("hello.txt");
if(!f1.exists()){
f1.createNewFile();
System.out.println("创建成功");
}else {
f1.delete();
System.out.println("删除成功");
}
File f1=new File("D:\\IO\\IO1");
System.out.println(f1.mkdir());
System.out.println(f1.mkdirs());
idea 单元测试,相对路径为Module下,main()测试,相对路径在当前的Project下
IO流
-
数据的输入输出,以流的方式进行
-
按操作数据单位不同分为:
- 字节流(8 bit)(非文本文件,图片,视频,.doc,.png,.mp4)
- 字符流(16 bit) (文本文件:.txt.cpp)
-
按数据流的流向不同分为:输入流(数据—>程序),输出流 (程序—>数据)
-
按流的角色的不同分为:节点流(直接处理文件),处理流(作用在已有的流上)
抽象基类 | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
节点流(文件流):FileInputStream FileOutputStream FileReader FileWriter
缓冲流(处理流的一种): BufferrdInputStream BufferrdOutputStream BufferrdReader BufferrdWriter
IO方法
- InputStream 和 Reader 是所有输入流的基类。
- InputStream(典型实现:FileInputStream)
- int read() 从输入流中读取数据的下一个字节 , 返回0到255范围内的int字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。
- int read(byte[] b)从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。否则以整数形式返回实际读取的字节数。
- int read(byte[] b, int off, int len) 将输入流中最多len个数据字节读入byte 数组。尝试读取len个字节,但读取的字节也可能小于该值。以整数形式返回实际读取的字节数。如果因为流位于文件末尾而没有可用的字节,则返回值 -1。
- void close()关闭此输入流并释放与该流关联的所有系统资源
- Reader(典型实现:FileReader)
- int read() 读取单个字符。作为整数读取的字符,范围在 0 到 65535 之间 (0x00-0xffff)(2个 字节的Unicode码),如果已到达流的末尾,则返回 -1
- int read(char [] c) 将字符读入数组。如果已到达流的末尾,则返回 -1。否则返回本次读取的字符数。
- int read(char [] c, int off, int len)将字符读入数组的某一部分。存到数组cbuf中,从off处开始存储,最多读len个字 符。如果已到达流的末尾,则返回 -1。否则返回本次读取的字符数。
- void close()关闭此输入流并释放与该流关联的所有系统资源
- InputStream(典型实现:FileInputStream)
- OutputStream 和 Writer 非常相似
- OutputStream
- void write(int b) 将指定的字节写入此输出流。写入0~255范围的。
- void write(byte[] b) 将 b.length 个字节从指定的 byte 数组写入此输出流。与调用 write(b, 0, b.length) 的效果完全相同。
- void write(byte[] b,int off,int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
- void flush()刷新此输出流并强制写出所有缓冲的输出字节,调用此方法指示应将这些字节立即写入它们预期的目标。
- void close()关闭此输出流并释放与该流关联的所有系统资源。
- Writer
- void write(int c) 写入单个字符。写入0 到 65535 之间的Unicode码。
- void write(char[] cbuf) 写入字符数组。
- void write(char[] cbuf,int off,int len) 写入字符数组的某一部分。从off开始,写入len个字符
- void write(String str) 写入字符串。
- void write(String str,int off,int len) 写入字符串的某一部分。
- void flush() 刷新该流的缓冲,则立即将它们写入预期目标。
- void close()关闭此输出流并释放与该流关联的所有系统资源
- OutputStream
节点流
FileReader
- read()返回读入的一个字符,如果达到文件末尾,返回-1
- read(char []cbuffer)返回每次读入字符的个数,读到末尾返回-1
- 读入的文件一定要存在,不然会报FileNotFoundException
-
- File类实例化
- FileReader流实例化
- 读入的操作
- 资源的关闭
@Test
public void testFileReader() {
FileReader fr= null;//提供具体流
try {
File file=new File("hello.txt");//相较于当前Module
fr = new FileReader(file);
//read()返回读入的一个字符,如果达到文件末尾,返回-1
int data;
while( (data= fr.read())!=-1){
System.out.print((char) data);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if(fr!=null)
fr.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Test
public void testFileReader1() {
FileReader fr = null;
try {
File file = new File("hello.txt");//相较于当前Module
fr = new FileReader(file);
char[] cbuffer = new char[5];
int len;
//read(char []cbuffer)返回每次读入字符的个数,读到末尾返回-1
while ((len = fr.read(cbuffer)) != -1) {
for (int i = 0; i < len; i++) {
System.out.print(cbuffer[i]);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if (fr != null)
fr.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
FileWriter
-
输出操作对应的File
- 如果不存在,自动创建
- 如果存在
- FileWriter(file,false) / new FileWriter(file)对原有文件覆盖,
- FileWriter(file,true) 不对源文件覆盖 , 实现文件后追加内容
-
- 提供File类的对象,指明写出到的文件
- 提供FileWriter对象,用于数据写出
- 写出的操作
- 流资源的关闭
@Test
public void testFileWriter() {
FileWriter fw= null;
try {
File file=new File("hello1.txt");
fw = new FileWriter(file,true);
fw.write("乐2333 aaa!");
fw.write("乐子人");
fw.write("乐2333 aaa!");
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if(fw!=null)
fw.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Test
public void testFileReaderFileWriter(){
FileReader fr = null;
FileWriter fw= null;
try {
File srcfile = new File("hello.txt");
File destfile = new File("hello1.txt");
fr = new FileReader(srcfile);
fw = new FileWriter(destfile);
char []charbuffer=new char[5];
int len;
while ((len=fr.read(charbuffer))!=-1){
fw.write(charbuffer,0,len);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if(fr!=null)
fr.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
if(fw!=null)
fw.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
FileInputStream
- int read(byte[] b)最多将b.length 个字节的数据读入一个 byte 数组中。如果因为已 经到达流末尾而没有可用的字节,则返回值 -1。否则以整数形式返回实际读取的字节数。
- int read() 从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。
FileOutputStream
@Test
public void FileInputOutputStream(){
long start = System.currentTimeMillis();
copyfile("E:\\pt\\是.大臣.1984圣诞特辑.Yes.Minister.Party.Games.SP.1984.1080P.WEB-DL.H264.AAC\\是.大臣.1984圣诞特辑.Yes.Minister.Party.Games.SP.part1.1984.1080P.WEB-DL.H264.AAC.mp4","E:\\pt\\test\\AAC.mp4");
long end = System.currentTimeMillis();
System.out.println(end-start);
}
public void copyfile(String srcpath,String destpath){
FileInputStream fi = null;
FileOutputStream fo= null;
try {
File srcfile = new File(srcpath);
File destfile = new File(destpath);
fi = new FileInputStream(srcfile);
fo = new FileOutputStream(destfile);
byte []bytebuffer=new byte[1024];
int len;
while ((len=fi.read(bytebuffer))!=-1){
fo.write(bytebuffer,0,len);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if(fi!=null)
fi.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
if(fo!=null)
fo.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
缓冲流
-
提高流读写效率
-
处理流 , 就是套接在已有流的基础上
-
BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter
-
缓冲区默认8192字节,先读到缓冲区
-
flush();手动刷新缓冲区 , 若缓冲区满自动刷新缓冲区
-
readLine()返回一行数据,或null
-
String str; while ((str=br.readLine())!=null){//返回一行数据,或null //bw.write(str+"\n");//str中不包含换行符 bw.write(str);//str中不包含换行符 bw.newLine(); }
-
-
- 提供File类的对象,指明写出到的文件
- 提供FileInputStream FileOutputStream对象
- 提供BufferedInputStream BufferedOutputStream对象
- 复制细节:读写
- 流资源的关闭 只需要关闭外层流,内层自动关闭
@Test
public void BufferedInputOutputStream(){
long start = System.currentTimeMillis();
copyfile("E:\\pt\\是.大臣.1984圣诞特辑.Yes.Minister.Party.Games.SP.1984.1080P.WEB-DL.H264.AAC\\是.大臣.1984圣诞特辑.Yes.Minister.Party.Games.SP.part1.1984.1080P.WEB-DL.H264.AAC.mp4","E:\\pt\\test\\AAC.mp4");
long end = System.currentTimeMillis();
System.out.println(end-start);
}
public void copyfile(String srcpath,String destpath){
BufferedInputStream bis=null;
BufferedOutputStream bos=null;
try {
File srcfile = new File(srcpath);
File destfile = new File(destpath);
FileInputStream fi = new FileInputStream(srcfile);//造节点流
FileOutputStream fo = new FileOutputStream(destfile);
bis=new BufferedInputStream(fi);//造缓冲流
bos=new BufferedOutputStream(fo);
byte []bytebuffer=new byte[1024];
int len;
while ((len=bis.read(bytebuffer))!=-1){
bos.write(bytebuffer,0,len);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if(bis!=null)
bis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
if(bos!=null)
bos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Test
public void testBufferedReaderWriter(){
BufferedReader br = null;
BufferedWriter bw= null;
try {
File srcfile = new File("hello.txt");
File destfile = new File("hello1.txt");
FileReader fr = new FileReader(srcfile);
FileWriter fw = new FileWriter(destfile);
br=new BufferedReader(fr);
bw=new BufferedWriter(fw);
//方式一
// char []charbuffer=new char[5];
// int len;
// while ((len=br.read(charbuffer))!=-1){
// bw.write(charbuffer,0,len);
// }
//方式二
String str;
while ((str=br.readLine())!=null){//返回一行数据,或null
//bw.write(str+"\n");//str中不包含换行符
bw.write(str);//str中不包含换行符
bw.newLine();
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if(br!=null)
br.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
if(bw!=null)
bw.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
对读取的数组进行异或再异或可以实现加密解密
转换流
- 提供了字符流和字节流的转换
- 字节流中的数据都是字符时,转成字符流操作更高效
- 使用转换流来处理文件乱码问题。实现编码和解码的功能。
- 处理字符集
- InputStreamReader:将InputStream转换为Reader 字节输入流–>字符输入流
- OutputStreamWriter:将Writer转换为OutputStream 字符输出流–>字节输出流
FileInputStream fis=new FileInputStream("hello.txt");//字节流
InputStreamReader isr=new InputStreamReader(fis);//使用系统默认字符集
InputStreamReader isr=new InputStreamReader(fis,"UTF-8");//使用指定字符集,取决去hello.txt保存时使用的字符集
public class InputstreamReader {
@Test
public void test() throws IOException {
FileInputStream fis=new FileInputStream("hello.txt");//字节流
InputStreamReader isr=new InputStreamReader(fis,"UTF-8");//使用指定字符集 字节流-->字符流
char []cbuf=new char[20];
int len;
while ((len=isr.read(cbuf))!=-1){
String str=new String(cbuf,0,len);
System.out.println(str);
}
isr.close();
}
}
@Test
public void test() throws IOException {
FileInputStream fis=new FileInputStream("hello.txt");//字节流
FileOutputStream fos=new FileOutputStream("hellogkb.txt");//字节流
InputStreamReader isr=new InputStreamReader(fis,"utf-8");//使用指定字符集 字节流-->字符流
OutputStreamWriter osw=new OutputStreamWriter(fos,"gbk");
char []cbuf=new char[20];
int len;
while ((len=isr.read(cbuf))!=-1){
osw.write(cbuf,0,len);
}
isr.close();
osw.close();
}
标准输入输出流
-
System.in和System.out分别代表了系统标准的输入和输出设备
-
System.in的类型是InputStream
-
System.out的类型是PrintStream,其是OutputStream的子类 FilterOutputStream 的子类
-
重定向:通过System类的setIn,setOut方法对默认设备进行改变。
-
public static void setIn(InputStream in)
-
public static void setOut(PrintStream out)
-
public static void main(String[] args) {
System.out.println("请输入信息(退出输入e或exit):");
// 把"标准"输入流(键盘输入)这个字节流包装成字符流,再包装成缓冲流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s = null;
try {
while ((s = br.readLine()) != null) { // 读取用户输入的一行数据 --> 阻塞程序
if ("e".equalsIgnoreCase(s) || "exit".equalsIgnoreCase(s)) {
System.out.println("安全退出!!");
break;
}
//将读取到的整行字符串转成大写输出
System.out.println("-->:" + s.toUpperCase());
System.out.println("继续输入信息");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close(); // 关闭过滤流时,会自动关闭它包装的底层节点流
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
打印流
- PrintStream和PrintWriter
- 提供了一系列重载的print()和println()方法,用于多种数据类型的输出
- PrintStream和PrintWriter的输出不会抛出IOException异常
- PrintStream和PrintWriter有自动flush功能
- PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。 在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。
- System.out返回的是PrintStream的实例
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream(new File("D:\\IO\\text.txt"));
// 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
ps = new PrintStream(fos, true);
if (ps != null) {// 把标准输出流(控制台输出)改成文件
System.setOut(ps);
}
for (int i = 0; i <= 255; i++) { // 输出ASCII字符
System.out.print((char) i);
if (i % 50 == 0) { // 每50个数据一行
System.out.println(); // 换行
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (ps != null) {
ps.close();
}
}
数据流
- 用于读取和写出基本数据类型、String类的数据
- DataInputStream 和 DataOutputStream 分别“套接”在 InputStream 和 OutputStream子类的流上
- 读取数据的顺序应该与写入的顺序相同
DataInputStream中的方法 boolean readBoolean() byte readByte() char readChar() float readFloat() double readDouble() short readShort() long readLong() int readInt() String readUTF() void readFully(byte[] b)
DataOutputStream中的方法 将上述的方法的read改为相应的write即可
DataOutputStream dos = null;
try { // 创建连接到指定文件的数据输出流对象
dos = new DataOutputStream(new FileOutputStream("destData.dat"));
dos.writeUTF("啊啊啊啊啊"); // 写UTF字符串
dos.writeBoolean(false); // 写入布尔值
dos.writeLong(1234567890L); // 写入长整数
System.out.println("写文件成功!");
} catch (IOException e) {
e.printStackTrace();
} finally { // 关闭流对象
try {
if (dos != null) {
// 关闭过滤流时,会自动关闭它包装的底层节点流
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
DataInputStream dis = null;
try {
dis = new DataInputStream(new FileInputStream("destData.dat"));
String info = dis.readUTF();
boolean flag = dis.readBoolean();
long time = dis.readLong();
System.out.println(info);
System.out.println(flag);
System.out.println(time);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (dis != null) {
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
对象流
序列化与反序列化
-
用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
-
序列化:用ObjectOutputStream类保存基本类型数据或对象的机制
-
反序列化:用ObjectInputStream类读取基本类型数据或对象的机制
-
对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从 而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
//序列化:将内存中的java对象保存在磁盘中或通过网络传输出去 ObjectOutputStream
@Test
public void outputtest(){
ObjectOutputStream oos= null;
try {
oos = new ObjectOutputStream(new FileOutputStream("hello.bat"));
oos.writeObject(new String("Java课程"));
oos.flush();//刷新操作
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(oos!=null){
try {
oos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
//反序列化:将磁盘文件中的需还原为内存中的一个java对象 ObjectInputStream
@Test
public void inputtest(){
ObjectInputStream ois= null;
try {
ois = new ObjectInputStream(new FileInputStream("hello.bat"));
Object obj= ois.readObject();
String str=(String) obj;
System.out.println(str);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {
if(ois!=null){
try {
ois.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
自定义类序列化
- 如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下Serializable(标识接口,空接口) Externalizable接口之一。 否则,会抛出NotSerializableException异常
- 当前类提供一个全局常量
public static final long serialVersionUID=47546353L;
表示序列化版本标识符- 声明为private public皆可
- serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象 进行版本控制,有关各版本反序列化时是否兼容。
- 如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化,无法还原
- 还应保证内部属性是可序列化的(默认情况下,基本数据类型都是可序列化的)
- ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量 , 将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中(存储为默认值)。
public class ThePerson implements Serializable {
public static final long serialVersionUID=47546353L;//定义序列版本号
}
oos.writeObject(new ThePerson("张三",18));
oos.flush();
ThePerson p= (ThePerson) ois.readObject();
System.out.println(p);
public class ThePerson implements Serializable {
public static final long serialVersionUID=47546353L;//定义序列版本号
private String name;
private int age;
private static int id;
private transient String othername;
public ThePerson() {
}
public ThePerson(String name, int age) {
this.name = name;
this.age = age;
}
public ThePerson(String name, int age, int id,String othername) {
this.name = name;
this.age = age;
this.othername = othername;
this.id=id;
}
@Override
public String toString() {
return "ThePerson{" +
"name='" + name + '\'' +
", age=" + age +
", id="+id+
", othername='" + othername + '\'' +
'}';
}
}
@Test
public void outputtest(){
ObjectOutputStream oos= null;
try {
oos = new ObjectOutputStream(new FileOutputStream("hello.bat"));
oos.writeObject(new String("Java课程"));
oos.flush();//刷新操作
oos.writeObject(new ThePerson("张三",18));
oos.flush();
oos.writeObject(new ThePerson("李四",18,111,"test"));
oos.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(oos!=null){
try {
oos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
@Test
public void inputtest(){
ObjectInputStream ois= null;
try {
ois = new ObjectInputStream(new FileInputStream("hello.bat"));
Object obj= ois.readObject();
String str=(String) obj;
System.out.println(str);
ThePerson p= (ThePerson) ois.readObject();
System.out.println(p);
p= (ThePerson) ois.readObject();
System.out.println(p);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {
if(ois!=null){
try {
ois.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
/*
Java课程
ThePerson{name='张三', age=18, id=0, othername='null'}
ThePerson{name='李四', age=18, id=0, othername='null'}
*/
随机存储文件流
-
RandomAccessFile 声明在java.io包下,但直接继承于java.lang.Object类。并且它实现了DataInput、DataOutput这两个接口,也就意味着这个类既可以读也可以写。
-
RandomAccessFile 类支持 “随机访问” 的方式,程序可以直接跳到文件的任意地方来读、写文件
- 支持只访问文件的部分内容
- 可以向已存在的文件后追加内容
raf2.seek(new File("hello1.txt").length());
写到文件末尾 - 作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建,写出到的文件存在,则对原有文件内容进行覆盖(默认从头覆盖)
-
RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。 RandomAccessFile 类对象可以自由移动记录指针:
- long getFilePointer():获取文件记录指针的当前位置
- void seek(long pos):将文件记录指针定位到 pos 位置
-
构造器
- public RandomAccessFile(File file, String mode)
- public RandomAccessFile(String name, String mode)
-
创建 RandomAccessFile 类实例需要指定一个 mode 参数,该参数指 定 RandomAccessFile 的访问模式:
- r: 以只读方式打开
- rw:打开以便读取和写入 ,数据不会立刻写到硬盘中
- rwd:打开以便读取和写入;同步文件内容的更新,数据立刻被写入硬盘,如果写数据过程发生异常,已被write的数据保存到硬盘,rw则全部丢失
- rws:打开以便读取和写入;同步文件内容和元数据的更新
-
如果模式为只读r。则不会创建文件,而是会去读取一个已经存在的文件, 如果读取的文件不存在则会出现异常。 如果模式为rw读写。如果文件不存在则会去创建文件
@Test
public void test(){
RandomAccessFile raf1= null;
RandomAccessFile raf2= null;
try {
raf1 = new RandomAccessFile(new File("pic.png"),"r");
raf2 = new RandomAccessFile(new File("pic1.png"),"rw");
byte[]mybuffer=new byte[512];
int len;
while ((len=raf1.read(mybuffer))!=-1){
//raf2.seek(new File("hello1.txt").length());写到文件末尾
raf2.write(mybuffer,0,len);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(raf1!=null){
try {
raf1.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(raf2!=null){
try {
raf2.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
//实现插入数据
@Test
public void test2(){
RandomAccessFile raf1= null;
try {
raf1 = new RandomAccessFile(new File("hello.txt"),"rw");
byte[]mybuffer=new byte[512];
int len;
StringBuilder builder=new StringBuilder((int)(new File("hello.txt").length()));
raf1.seek(3);
while ((len=raf1.read(mybuffer))!=-1){
builder.append(new String(mybuffer,0,len));//保存指针后面所有数据到StringBuilder中
}
raf1.seek(3);
raf1.write("插入数据".getBytes());//获取byte,覆盖指针后的位置
raf1.write(builder.toString().getBytes());//追加保存的数据
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (raf1 != null) {
try {
raf1.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
NIO
- NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的(IO是面向流的)、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。
- Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO
Path
File大多数方法在出错时仅返回失败,并不会提供异常信息 . 在NIO. 2,引入了Path接口,代表一个平台无关的平台路径,描述了目录结构中文件的位置。Path可以看成是File类的升级版本,实际引用的资源也可以不存在。
File file = new File("index.html");
Path path = Paths.get("index.html");
Paths
Paths类提供的静态 get() 方法用来获取 Path 对象:
-
static Path get(String first, String … more) : 用于将多个字符串串连成路径
-
static Path get(URI uri): 返回指定uri对应的Path路径
Path 常用方法:
String toString() : 返回调用 Path 对象的字符串表示形式
boolean startsWith(String path) : 判断是否以 path 路径开始
boolean endsWith(String path) : 判断是否以 path 路径结束
boolean isAbsolute() : 判断是否是绝对路径
Path getParent() :返回Path对象包含整个路径,不包含 Path 对象指定的文件路径
Path getRoot() :返回调用 Path 对象的根路径
Path getFileName() : 返回与调用 Path 对象关联的文件名
int getNameCount() : 返回Path 根目录后面元素的数量
Path getName(int idx) : 返回指定索引位置 idx 的路径名称
Path toAbsolutePath() : 作为绝对路径返回调用 Path 对象
Path resolve(Path p) :合并两个路径,返回合并后的路径对应的Path对象
File toFile(): 将Path转化为File类的对象
Files常用方法:
Path copy(Path src, Path dest, CopyOption … how) : 文件的复制
Path createDirectory(Path path, FileAttribute<?> … attr) : 创建一个目录
Path createFile(Path path, FileAttribute<?> … arr) : 创建一个文件
void delete(Path path) : 删除一个文件/目录,如果不存在,执行报错
void deleteIfExists(Path path) : Path对应的文件/目录如果存在,执行删除
Path move(Path src, Path dest, CopyOption…how) : 将 src 移动到 dest 位置
long size(Path path) : 返回 path 指定文件的大小
Files常用方法:用于判断
boolean exists(Path path, LinkOption … opts) : 判断文件是否存在
boolean isDirectory(Path path, LinkOption … opts) : 判断是否是目录
boolean isRegularFile(Path path, LinkOption … opts) : 判断是否是文件
boolean isHidden(Path path) : 判断是否是隐藏文件
boolean isReadable(Path path) : 判断文件是否可读
boolean isWritable(Path path) : 判断文件是否可写
boolean notExists(Path path, LinkOption … opts) : 判断文件是否不存在
Files常用方法:用于操作内容
SeekableByteChannel newByteChannel(Path path, OpenOption…how) : 获取与指定文件的连接,how 指定打开方式。
DirectoryStream<Path> newDirectoryStream(Path path) : 打开 path 指定的目录
InputStream newInputStream(Path path, OpenOption…how):获取 InputStream 对象
OutputStream newOutputStream(Path path, OpenOption…how) : 获取 OutputStream 对象
导入第三方jar
模块下创建lib目录,jar包复制到对应路径下
添加为库
网络编程
-
IP 地址:InetAddress类代表ip
-
localhost 127.0.0.1
-
端口号与IP地址的组合得出一个网络套接字:Socket
-
公认端口:0~1023。被预先定义的服务通信占用(如:HTTP占用端口 80,FTP占用端口21,Telnet占用端口23)
-
注册端口:1024~49151。分配给用户进程或应用程序。
-
动态/私有端口:49152~65535。
InetAdress
- InetAddress类没有提供公共的构造器,而是提供了如下几个静态方法来获取 InetAddress实例
- public static InetAddress getLocalHost()
- public static InetAddress getByName(String host)
- InetAddress提供了如下几个常用的方法
- public String getHostAddress():返回 IP 地址字符串(以文本表现形式)。
- public String getHostName():获取此 IP 地址的主机名
- public boolean isReachable(int timeout):测试是否可以达到该地址
//实例化
try {
InetAddress inet1=InetAddress.getByName("192.168.123.8");
System.out.println(inet1);///192.168.123.8
InetAddress inet2=InetAddress.getByName("blog.lrdhappy.com");
System.out.println(inet2);//blog.lrdhappy.com/101.43.108.114
InetAddress inet3=InetAddress.getByName("localhost");
System.out.println(inet3);//localhost/127.0.0.1
InetAddress inet4=InetAddress.getLocalHost();
System.out.println(inet4);//DESKTOP-DOFL4VM/192.168.6.1
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
//getHostAddress() getHostName()
System.out.println("HostName "+inet2.getHostName()+" HostAddress "+inet2.getHostAddress());
//HostName blog.lrdhappy.com HostAddress 101.43.108.114
Socket
-
Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输
-
Socket分类:
- 流套接字(stream socket):使用TCP提供可依赖的字节流服务
- 数据报套接字(datagram socket):使用UDP提供“尽力而为”的数据报服务
-
ServerSocket 对象负责等待客户端请求建立套接字连接,服务器必须事先建立一个等待客户请求建立套接字连接的ServerSocket对象。
-
接收客户的套接字请求,就是accept()方法会返回一个 Socket 对象
Socket类的常用构造器:
public Socket(InetAddress address,int port)创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
public Socket(String host,int port)创建一个流套接字并将其连接到指定主机上的指定端口号。
Socket类的常用方法:
public InputStream getInputStream()返回此套接字的输入流。可以用于接收网络消息
public OutputStream getOutputStream()返回此套接字的输出流。可以用于发送网络消息
public InetAddress getInetAddress()此套接字连接到的远程 IP 地址;如果套接字是未连接的,则返回 null。
public InetAddress getLocalAddress()获取套接字绑定的本地地址。 即本端的IP地址
public int getPort()此套接字连接到的远程端口号;如果尚未连接套接字,则返回 0。
public int getLocalPort()返回此套接字绑定到的本地端口。 如果尚未绑定套接字,则返回 -1。即本端的端口号。
public void close()关闭此套接字。套接字被关闭后,便不可在以后的网络连接中使用(即无法重新连接或重新绑定)。需要创建新的套接字对象。 关闭此套接字也将会关闭该套接字的 InputStream 和OutputStream。
public void shutdownInput()如果在套接字上调用 shutdownInput() 后从套接字输入流读取内容,则流将返回 EOF(文件结束符)。 即不能在从此套接字的输入流中接收任何数据。
public void shutdownOutput()禁用此套接字的输出流。对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列。 如果在套接字上调用 shutdownOutput() 后写入套接字输出流,则该流将抛出 IOException。 即不能通过此套接字的输出流发送任何数据。
- 创建 Socket:根据指定服务端的 IP 地址或端口号构造 Socket 类对象。若服务器端响应,则建立客户端到服务器的通信线路。若连接失败,会出现异常。
- 打开连接到 Socket 的输入/出流: 使用 getInputStream()方法获得输入流,使用 getOutputStream()方法获得输出流,进行数据传输
- 按照一定的协议对 Socket 进行读/写操作:通过输入流读取服务器放入线路的信息 (但不能读取自己放入线路的信息),通过输出流将信息写入线程。
- 关闭 Socket:断开客户端到服务器的连接,释放线路
TCP
- 客户端
- 创建Socket对象,指明服务器的ip端口号
- 获取一个输出流,用于输出数据
- 写出数据
- 资源关闭
- 服务端
- 创建ServerSocket,指明自己的端口号
- 调用accept(),表明可以接收来自客户端的socket
- 获取输入流
- 读取输入流的数据
- 资源关闭
//传输文本
@Test
public void client(){
Socket socket= null;
OutputStream os= null;
try {
InetAddress inet=InetAddress.getByName("49.233.55.230");
socket = new Socket(inet,5432);
os = socket.getOutputStream();
os.write("hello server!美好的一天".getBytes());//字符转换为字节
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(os!=null){
try {
os.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
@Test
public void server(){
ServerSocket serversocket= null;
Socket socket= null;
InputStream is= null;
ByteArrayOutputStream baos= null;
try {
serversocket = new ServerSocket(5432);
socket = serversocket.accept();
is = socket.getInputStream();
baos = new ByteArrayOutputStream();
byte[] buffer=new byte[5];
int len;
while ((len=is.read(buffer))!=-1){
baos.write(buffer,0,len);
}
System.out.println(baos.toString());//存储后一次性输出,避免中文字符被切断
System.out.println("收到了来自于:"+socket.getInetAddress().getHostAddress());
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(baos!=null){
try {
baos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(is!=null){
try {
is.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(serversocket!=null){
try {
serversocket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
// while ((len=is.read(buffer))!=-1){
//// String str=new String(buffer,0,len);
//// System.out.println(str);
//// }
}
//传输图片,并返回接收成功信息
@Test
public void client2(){
Socket socket= null;
OutputStream os= null;
FileInputStream fis=null;
InputStream is=null;
ByteArrayOutputStream baos=null;
try {
InetAddress inet=InetAddress.getByName("49.233.55.230");
socket = new Socket(inet,5432);
os = socket.getOutputStream();
fis=new FileInputStream(new File("class.png"));
byte[] buffer=new byte[512];
int len;
while ((len=fis.read(buffer))!=-1){
os.write(buffer,0,len);
}
socket.shutdownOutput();//关闭数据的输出
is=socket.getInputStream();
int len1;
baos=new ByteArrayOutputStream();
byte []newbuffer=new byte[16];
while ((len1=is.read(newbuffer))!=-1){
baos.write(newbuffer,0,len1);
}
System.out.println(baos.toString());
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(os!=null){
try {
os.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(is!=null){
try {
is.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(baos!=null){
try {
baos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
@Test
public void server2(){
ServerSocket serversocket= null;
Socket socket= null;
InputStream is= null;
FileOutputStream fos=null;
OutputStream os=null;
try {
serversocket = new ServerSocket(5432);
socket = serversocket.accept();
is = socket.getInputStream();
fos=new FileOutputStream(new File("serverpic.png"));
byte[] buffer=new byte[512];
int len;
while ((len=is.read(buffer))!=-1){
fos.write(buffer,0,len);
}
System.out.println("收到了来自于:"+socket.getInetAddress().getHostAddress());
os=socket.getOutputStream();
os.write("成功接收图片".getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(is!=null){
try {
is.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(serversocket!=null){
try {
serversocket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(os!=null){
try {
os.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
UDP
- 类 DatagramSocket 和 DatagramPacket 实现了基于 UDP 协议网络程序。
- UDP数据报通过数据报套接字 DatagramSocket 发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。
- DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号。
- UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方的连接。
DatagramSocket 类的常用方法
public DatagramSocket(int port)创建数据报套接字并将其绑定到本地主机上的指定端口。套接字将被绑定到通配符地址,IP 地址由内核来选择。
public DatagramSocket(int port,InetAddress laddr)创建数据报套接字,将其绑定到指定的本地地址。本地端口必须在 0 到 65535 之间(包括两者)。如果 IP 地址为 0.0.0.0,套接字将被绑定到通配符地址,IP 地址由内核选择。
public void close()关闭此数据报套接字。
public void send(DatagramPacket p)从此套接字发送数据报包。DatagramPacket 包含的信息指示:将要发送的数据、其长度、远程主机的 IP 地址和远程主机的端口号。
public void receive(DatagramPacket p)从此套接字接收数据报包。当此方法返回时,DatagramPacket的缓冲区填充了接收的数据。数据报包也包含发送方的 IP 地址和发送方机器上的端口号。 此方法在接收到数据报前一直阻塞。数据报包对象的 length 字段包含所接收信息的长度。如果信息比包的长度长,该信息将被截短。
public InetAddress getLocalAddress()获取套接字绑定的本地地址。
public int getLocalPort()返回此套接字绑定的本地主机上的端口号。
public InetAddress getInetAddress()返回此套接字连接的地址。如果套接字未连接,则返回 null。
public int getPort()返回此套接字的端口。如果套接字未连接,则返回 -1。
DatagramPacket类的常用方法
public DatagramPacket(byte[] buf,int length)构造 DatagramPacket,用来接收长度为 length 的数据包。 length 参数必须小于等于 buf.length。
public DatagramPacket(byte[] buf,int length,InetAddress address,int port)构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。length参数必须小于等于 buf.length。
public InetAddress getAddress()返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。
public int getPort()返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。
public byte[] getData()返回数据缓冲区。接收到的或将要发送的数据从缓冲区中的偏移量 offset 处开始,持续 length 长度。
public int getLength()返回将要发送或接收到的数据的长度。
@Test
public void send() throws IOException {
DatagramSocket socket = new DatagramSocket();
String str="UDP方式传输数据";
byte[]data=str.getBytes();
InetAddress inet=InetAddress.getByName("127.0.0.1");
DatagramPacket packet = new DatagramPacket(data,0,data.length,inet,5432);
socket.send(packet);
socket.close();
}
@Test
public void receive() throws IOException {
DatagramSocket socket=new DatagramSocket(5432);
byte[]data=new byte[1024];
DatagramPacket packet = new DatagramPacket(data,0,data.length);
socket.receive(packet);
System.out.println(new String(packet.getData(),0,packet.getLength()));//获取读到的数据和长度
socket.close();
}
URL
- URL(Uniform Resource Locator):统一资源定位符,它表示 Internet 上某一 资源的地址。
- <传输协议>://<主机名>:<端口号>/<文件名>#片段名?参数列表
public URL (String spec):通过一个表示URL地址的字符串可以构造一个URL对象。例如:URL url = new URL ("http://www. atguigu.com/");
public URL(URL context, String spec):通过基 URL 和相对 URL 构造一个 URL 对象。
URL downloadUrl = new URL(url, “download.html")
public URL(String protocol, String host, String file); 例如:new URL("http", "www.atguigu.com", “download. html");
public URL(String protocol, String host, int port, String file); 例如: URL gamelan = new URL("http", "www.atguigu.com", 80, “download.html");
public String getProtocol( ) 获取该URL的协议名
public String getHost( ) 获取该URL的主机名
public String getPort( ) 获取该URL的端口号
public String getPath( ) 获取该URL的文件路径
public String getFile( ) 获取该URL的文件名
public String getQuery( ) 获取该URL的查询名
public static void main(String[] args){
HttpsURLConnection urlConnection = null;//获取url连接
InputStream inputStream = null;//获取输入流
FileOutputStream fos= null;
try {
URL url=new URL("https://lrd12345.oss-cn-beijing.aliyuncs.com/img/icon.jpg");
System.out.println(url.getPath());
System.out.println(url.getAuthority());
System.out.println(url.getProtocol());
urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.connect();//获取连接
inputStream = urlConnection.getInputStream();
fos = new FileOutputStream(new File("icon.jpg"));
byte []buf=new byte[1024];
int len;
while ((len=inputStream.read(buf))!=-1){
fos.write(buf,0,len);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(urlConnection!=null){
urlConnection.disconnect();//断开连接
}
}
}
反射
简介
- Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
- 体现动态性
- Java反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
- java.lang.Class:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造器
//反射之前的操作: 在Person类外部,不可以通过Person类的对象调用其内部私有结构
//反射后的操作
//调用构造器
Class clazz=Person.class;
Constructor cons = clazz.getConstructor(String.class, int.class);
Object obj=cons.newInstance("TOM",12);
Person p=(Person) obj;
System.out.println(p);
//调用属性
Field age = clazz.getDeclaredField("age");
age.set(p,10);
System.out.println(p);
//调用方法
Method show = clazz.getDeclaredMethod("show");//可以添加参数
show.invoke(p);//可以添加参数
//调用私有构造器
Constructor cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessible(true);
Person p1 = (Person) cons1.newInstance("Jack");
System.out.println(p1);
//调用私有属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p1,"Jee");
System.out.println(p1);
//调用私有方法
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
Object nation = showNation.invoke(p1, "中国");
System.out.println(nation);
大多数情况,建议使用直接new方式 , 动态的问题使用反射
Class
-
java.lang.Class
-
类的加载过程
- javac 生成字节码文件.class
- java 对某个字节码文件进行解释运行,加载到内存中
- 加载到内存中的类,为运行时类,是Class类的一个实例
- 加载到内存中的运行时类,会缓存一定的时间,可以通过不同的方式获取此运行时类
-
可以有Class对象的类型
- class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
- interface:接口
- [ ]:数组
- enum:枚举
- annotation:注解@interface
- primitive type:基本数据类型
- void
-
JVM垃圾回收机制可以回收Class对象
获取Class实例方式
//方式一 调用运行时类的属性.class
Class clazz1=Person.class;//可以为泛型
//方式二:通过运行时类的对象
Person p1=new Person();
Class clazz2=p1.getClass();
//方式三:调用Class的静态方法:forName(String classPath) 常用
Class clazz3 = Class.forName("test.lrd.com.Person");
System.out.println(clazz1==clazz2);//true
System.out.println(clazz1==clazz3);//true
//方式四: 使用类的加载器 ClassLoader 了解
ClassLoader classLoader=Reflectiontest.class.getClassLoader();
Class<?> clazz4 = classLoader.loadClass("test.lrd.com.Person");
System.out.println(clazz4);
System.out.println(clazz1==clazz4);//true
方法名 | 功能说明 |
---|---|
static Class forName(String name) | 返回指定类名 name 的 Class 对象 |
Object newInstance() | 调用缺省构造函数,返回该Class对象的一个实例 |
getName() | 返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称 |
Class getSuperClass() | 返回当前Class对象的父类的Class对象 |
Class [] getInterfaces() | 获取当前Class对象的接口 |
ClassLoader getClassLoader() | 返回该类的类加载器 |
Class getSuperclass() | 返回表示此Class所表示的实体的超类的Class |
Constructor[] getConstructors() | 返回一个包含某些Constructor对象的数组 |
Field[] getDeclaredFields() | 返回Field对象的一个数组 |
Method getMethod(String name,Class … paramTypes) | 返回一个Method对象,此对象的形参类型为paramType |
Properties pros=new Properties();
//方式一 识别文件位置默认在模块下
// FileInputStream fis=new FileInputStream("jdbc.properties");
// pros.load(fis);
//方式二 使用ClassLoader 识别文件位置默认在模块的src下
ClassLoader classLoader = Reflectiontest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("jdbc.properties");
pros.load(is);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
System.out.println("user="+user+" password="+password);
创建运行时类的对象
Class clazz=Person.class;
/*
newInstance() 调用此方法,创建对应的运行时类的对象,调用运行时类的空参构造器
要求:
1.运行时类必须提供空参构造器
2.构造器权限足以访问,空参构造器通常设置为public
在javabean中要求提供public空参构造器:
1.便于反射,创建运行时类的对象
2.便于子类继承此运行时类时,默认调用super(),保证父类拥有此构造器
*/
Person obj = (Person) clazz.newInstance();
System.out.println(obj);
获取运行时类的完整结构
使用反射可以取得:
-
实现的全部接口
- public Class[] getInterfaces() 确定此对象所表示的类或接口实现的接口。
-
所继承的父类
- public Class getSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。
-
全部的构造器
- public Constructor[] getConstructors() 返回此 Class 对象所表示的类的所有public构造方法。
- public Constructor[] getDeclaredConstructors() 返回此 Class 对象表示的类声明的所有构造方法。
- Constructor类中:
- 取得修饰符: public int getModifiers();
- 取得方法名称: public String getName();
- 取得参数的类型:public Class[] getParameterTypes();
-
全部的方法
- public Method[] getDeclaredMethods() 返回此Class对象所表示的类或接口的全部方法
- public Method[] getMethods() 返回此Class对象所表示的类或接口的public的方法
- Method类中:
- public Class getReturnType()取得全部的返回值
- public Class[] getParameterTypes()取得全部的参数
- public int getModifiers()取得修饰符
- public Class[] getExceptionTypes()取得异常信息
-
全部的Field
- public Field[] getFields() 返回此Class对象所表示的类及其父类或接口的public的Field
- public Field[] getDeclaredFields() 返回此Class对象所表示的类或接口的全部Field(不包含父类)
- Field方法中:
- public int getModifiers() 以整数形式返回此Field的修饰符
- public Class getType() 得到Field的属性类型
- public String getName() 返回Field的名称。
-
Annotation相关
- get Annotation(Class annotationClass)
- getDeclaredAnnotations()
-
泛型相关 获取父类泛型类型:
- Type getGenericSuperclass() 泛型类型:
- ParameterizedType 获取实际的泛型类型参数数组:
- getActualTypeArguments()
-
类所在的包
- Package getPackage()
public class FieldTest {
@Test
public void test1(){
Class clazz = Person.class;
//获取属性结构
//getFields():获取当前运行时类及其父类中声明为public访问权限的属性
Field[] fields = clazz.getFields();
for(Field f : fields){
System.out.println(f);
}
System.out.println();
//getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
Field[] declaredFields = clazz.getDeclaredFields();
for(Field f : declaredFields){
System.out.println(f);
}
}
//权限修饰符 数据类型 变量名
@Test
public void test2(){
Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for(Field f : declaredFields){
//1.权限修饰符
int modifier = f.getModifiers();
System.out.print(Modifier.toString(modifier) + "\t");
//2.数据类型
Class type = f.getType();
System.out.print(type.getName() + "\t");
//3.变量名
String fName = f.getName();
System.out.print(fName);
System.out.println();
}
}
}
public class MethodTest {
@Test
public void test1(){
Class clazz = Person.class;
//getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
Method[] methods = clazz.getMethods();
for(Method m : methods){
System.out.println(m);
}
System.out.println();
//getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
Method[] declaredMethods = clazz.getDeclaredMethods();
for(Method m : declaredMethods){
System.out.println(m);
}
}
/*
@Xxxx
权限修饰符 返回值类型 方法名(参数类型1 形参名1,...) throws XxxException{}
*/
@Test
public void test2(){
Class clazz = Person.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for(Method m : declaredMethods){
//1.获取方法声明的注解
Annotation[] annos = m.getAnnotations();
for(Annotation a : annos){
System.out.println(a);
}
//2.权限修饰符
System.out.print(Modifier.toString(m.getModifiers()) + "\t");
//3.返回值类型
System.out.print(m.getReturnType().getName() + "\t");
//4.方法名
System.out.print(m.getName());
System.out.print("(");
//5.形参列表
Class[] parameterTypes = m.getParameterTypes();
if(!(parameterTypes == null && parameterTypes.length == 0)){
for(int i = 0;i < parameterTypes.length;i++){
if(i == parameterTypes.length - 1){
System.out.print(parameterTypes[i].getName() + " args_" + i);
break;
}
System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
}
}
System.out.print(")");
//6.抛出的异常
Class[] exceptionTypes = m.getExceptionTypes();
if(exceptionTypes.length > 0){
System.out.print("throws ");
for(int i = 0;i < exceptionTypes.length;i++){
if(i == exceptionTypes.length - 1){
System.out.print(exceptionTypes[i].getName());
break;
}
System.out.print(exceptionTypes[i].getName() + ",");
}
}
System.out.println();
}
}
}
public class OtherTest {
/*
获取构造器结构
*/
@Test
public void test1(){
Class clazz = Person.class;
//getConstructors():获取当前运行时类中声明为public的构造器(不包括父类)
Constructor[] constructors = clazz.getConstructors();
for(Constructor c : constructors){
System.out.println(c);
}
System.out.println();
//getDeclaredConstructors():获取当前运行时类中声明的所有的构造器(不包括父类)
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for(Constructor c : declaredConstructors){
System.out.println(c);
}
}
/*
获取运行时类的父类
*/
@Test
public void test2(){
Class clazz = Person.class;
Class superclass = clazz.getSuperclass();
System.out.println(superclass);
}
/*
获取运行时类的带泛型的父类
*/
@Test
public void test3(){
Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);
}
/*
获取运行时类的带泛型的父类的泛型
代码:逻辑性代码 vs 功能性代码
*/
@Test
public void test4(){
Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
//获取泛型类型
Type[] actualTypeArguments = paramType.getActualTypeArguments();
// System.out.println(actualTypeArguments[0].getTypeName());
System.out.println(((Class)actualTypeArguments[0]).getName());
}
/*
获取运行时类实现的接口
*/
@Test
public void test5(){
Class clazz = Person.class;
Class[] interfaces = clazz.getInterfaces();
for(Class c : interfaces){
System.out.println(c);
}
System.out.println();
//获取运行时类的父类实现的接口
Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
for(Class c : interfaces1){
System.out.println(c);
}
}
/*
获取运行时类所在的包
*/
@Test
public void test6(){
Class clazz = Person.class;
Package pack = clazz.getPackage();
System.out.println(pack);
}
/*
获取运行时类声明的注解
*/
@Test
public void test7(){
Class clazz = Person.class;
Annotation[] annotations = clazz.getAnnotations();
for(Annotation annos : annotations){
System.out.println(annos);
}
}
}
调用运行时类的指定结构
public class ReflectionTest {
@Test
public void testField() throws Exception {
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//获取指定的属性:要求运行时类中属性声明为public
//通常不采用此方法
Field id = clazz.getField("id");
/*
设置当前属性的值
set():参数1:指明设置哪个对象的属性 参数2:将此属性值设置为多少
*/
id.set(p,1001);
/*
获取当前属性的值
get():参数1:获取哪个对象的当前属性值
*/
int pId = (int) id.get(p);
System.out.println(pId);
}
@Test
public void testField1() throws Exception {
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
Field name = clazz.getDeclaredField("name");
//2.保证当前属性是可访问的
name.setAccessible(true);
//3.获取、设置指定对象的此属性值
name.set(p,"Tom");
System.out.println(name.get(p));
}
@Test
public void testMethod() throws Exception {
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
/*
1.获取指定的某个方法
getDeclaredMethod():参数1 :指明获取的方法的名称 参数2:指明获取的方法的形参列表
*/
Method show = clazz.getDeclaredMethod("show", String.class);
//2.保证当前方法是可访问的
show.setAccessible(true);
/*
3. 调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
invoke()的返回值即为对应类中调用的方法的返回值。
*/
Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
System.out.println(returnValue);
System.out.println("*************如何调用静态方法*****************");
// private static void showDesc()
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
//如果调用的运行时类中的方法没有返回值,则此invoke()返回null
//Object returnVal = showDesc.invoke(null);
Object returnVal = showDesc.invoke(Person.class);
System.out.println(returnVal);//null
}
/*
如何调用运行时类中的指定的构造器
*/
@Test
public void testConstructor() throws Exception {
Class clazz = Person.class;
//private Person(String name)
/*
1.获取指定的构造器
getDeclaredConstructor():参数:指明构造器的参数列表
*/
Constructor constructor = clazz.getDeclaredConstructor(String.class);
//2.保证此构造器是可访问的
constructor.setAccessible(true);
//3.调用此构造器创建运行时类的对象
Person per = (Person) constructor.newInstance("Tom");
System.out.println(per);
}
}
动态代理
//代理模式 静态代理 代理类和被代理类编译期间确定
interface ClothFactory{
void produceCloth();
}
class ProxyClothFactory implements ClothFactory{
private ClothFactory factory;
public ProxyClothFactory(ClothFactory factory) {
this.factory = factory;
}
@Override
public void produceCloth() {
System.out.println("代理工厂运营---");
factory.produceCloth();
System.out.println("代理工厂工作结束");
}
}
class Factorytrue implements ClothFactory{
@Override
public void produceCloth() {
System.out.println("被代理工厂工作");
}
}
public class StaticProxytest {
@Test
public void test(){
Factorytrue ft=new Factorytrue();//创建被代理类对象
ProxyClothFactory proxyClothFactory = new ProxyClothFactory(ft);//创建代理类对象
proxyClothFactory.produceCloth();
}
}
//动态代理
interface Human{
String getBelief();
void eat(String food);
}
class SuperMan implements Human{//被代理类
@Override
public String getBelief() {
return "我可以飞";
}
@Override
public void eat(String food) {
System.out.println("我喜欢吃"+food);
}
}
class ProxyFactory{
//调用方法 返回一个代理类对象
public static Object getProxyInstance(Object obj){//obj被代理类对象
MyinvocationHandler handler=new MyinvocationHandler();
handler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
}
}
class MyinvocationHandler implements InvocationHandler{
private Object obj;//需要使用被代理类对象赋值
public void bind(Object obj){
this.obj=obj;
}
//当通过代理类对象,调用方法a时,自动调用如下的invoke()
//将被代理类要执行a的方法,声明在invoke中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue = method.invoke(obj, args);//method: 代理类对象调用的方法此方法也作为被代理类要调用的方法
return returnValue;//上述方法的返回值
}
}
public class ProxyTest {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);//proxyInstance 代理类对象
System.out.println(proxyInstance.getBelief());
proxyInstance.eat("烧烤");
Factorytrue factorytrue = new Factorytrue();
ClothFactory proxyInstance1 = (ClothFactory) ProxyFactory.getProxyInstance(factorytrue);
proxyInstance1.produceCloth();
}
}
AOP
面向切面编程Aspect Orient Programming
***********************************
*动态代理增加的通用方法1 *
* *
*回调目标对象的方法(可替换为不同对象)*
* *
*动态代理增加的通用方法2 *
***********************************
class HumanUtil{
public void method1(){
System.out.println("====================通用方法一====================");
}
public void method2(){
System.out.println("====================通用方法二====================");
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
HumanUtil util = new HumanUtil();
util.method1();
//method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
//obj:被代理类的对象
Object returnValue = method.invoke(obj,args);
util.method2();
//上述方法的返回值就作为当前类中的invoke()的返回值。
return returnValue;
}
Q.E.D.