集合

集合框架

  • 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 可以按照添加对象的指定属性排序
  • Map接口:双列数据,保存具有映射的关系的键值对

    • HashMap 作为Map的主要实现类 线程不安全 , 效率高 可以存储null的key和value
      • LinkedHashMap 保证在遍历map元素时,可以按照添加顺序实现遍历,频繁遍历时,效率高
    • TreeMap 保证按照key-value对排序,实现对key的自然排序和定制排序 底层使用红黑树
    • Hashtable 作为Map的古老实现类 线程安全 , 效率低 不可以存储null的key和value
      • Properties 常用来处理配置文件,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

有限制条件的通配符

  • (无穷小 , Number]只允许泛型为Number及Number子类的引用调用
  • [Number , 无穷大) 只允许泛型为Number及Number父类的引用调用
  • 只允许泛型为实现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()关闭此输入流并释放与该流关联的所有系统资源
  • 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()关闭此输出流并释放与该流关联的所有系统资源

节点流

FileReader

  • read()返回读入的一个字符,如果达到文件末尾,返回-1
  • read(char []cbuffer)返回每次读入字符的个数,读到末尾返回-1
  • 读入的文件一定要存在,不然会报FileNotFoundException
    1. File类实例化
    2. FileReader流实例化
    3. 读入的操作
    4. 资源的关闭
@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) 不对源文件覆盖 , 实现文件后追加内容
    1. 提供File类的对象,指明写出到的文件
    2. 提供FileWriter对象,用于数据写出
    3. 写出的操作
    4. 流资源的关闭
@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();
      }
      
    1. 提供File类的对象,指明写出到的文件
    2. 提供FileInputStream FileOutputStream对象
    3. 提供BufferedInputStream BufferedOutputStream对象
    4. 复制细节:读写
    5. 流资源的关闭 只需要关闭外层流,内层自动关闭
@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。 即不能通过此套接字的输出流发送任何数据。
  1. 创建 Socket:根据指定服务端的 IP 地址或端口号构造 Socket 类对象。若服务器端响应,则建立客户端到服务器的通信线路。若连接失败,会出现异常。
  2. 打开连接到 Socket 的输入/出流: 使用 getInputStream()方法获得输入流,使用 getOutputStream()方法获得输出流,进行数据传输
  3. 按照一定的协议对 Socket 进行读/写操作:通过输入流读取服务器放入线路的信息 (但不能读取自己放入线路的信息),通过输出流将信息写入线程。
  4. 关闭 Socket:断开客户端到服务器的连接,释放线路

TCP

  • 客户端
    1. 创建Socket对象,指明服务器的ip端口号
    2. 获取一个输出流,用于输出数据
    3. 写出数据
    4. 资源关闭
  • 服务端
    1. 创建ServerSocket,指明自己的端口号
    2. 调用accept(),表明可以接收来自客户端的socket
    3. 获取输入流
    4. 读取输入流的数据
    5. 资源关闭
//传输文本

@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);

获取运行时类的完整结构

使用反射可以取得:

  1. 实现的全部接口

    • public Class[] getInterfaces() 确定此对象所表示的类或接口实现的接口。
  2. 所继承的父类

    • public Class getSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。
  3. 全部的构造器

    • public Constructor[] getConstructors() 返回此 Class 对象所表示的类的所有public构造方法。
    • public Constructor[] getDeclaredConstructors() 返回此 Class 对象表示的类声明的所有构造方法。
    • Constructor类中:
      • 取得修饰符: public int getModifiers();
      • 取得方法名称: public String getName();
      • 取得参数的类型:public Class[] getParameterTypes();
  4. 全部的方法

    • public Method[] getDeclaredMethods() 返回此Class对象所表示的类或接口的全部方法
    • public Method[] getMethods() 返回此Class对象所表示的类或接口的public的方法
    • Method类中:
      • public Class getReturnType()取得全部的返回值
      • public Class[] getParameterTypes()取得全部的参数
      • public int getModifiers()取得修饰符
      • public Class[] getExceptionTypes()取得异常信息
  5. 全部的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的名称。
  6. Annotation相关

    • get Annotation(Class annotationClass)
    • getDeclaredAnnotations()
  7. 泛型相关 获取父类泛型类型:

    • Type getGenericSuperclass() 泛型类型:
    • ParameterizedType 获取实际的泛型类型参数数组:
    • getActualTypeArguments()
  8. 类所在的包

    • 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;
}