Java 学习日记(3)

JAVA 集合

一方面,面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象的操作,就要对对象进行存储。另一方面,使用 Array 存储对象具有一些弊端,而Java集合就像一种容器,可以动态地把多个对象的引用放入容器中。

  • 数组在内存存储方面的特点
    • 数组初始化后,长度就确定了。
    • 数组声明的类型,就决定了进行元素初始化时的类型
  • 数组在存储数据时的弊端
    • 数组长度确定,不易扩展
    • 数组中提供的属性和方法少
    • 数组存储的数据是有序的,可以重复的

JAVA 集合类可以用来存储数量不等的多个对象,还可以用于保存具有映射关系的关联数组。

package com.an.java;

import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;

/**
 * 一、集合框架的概述
 * 1、集合、数组都是对多个数据进行存储操作的结构,简称Java容器
 * 说明: 此时的存储,主要指的是内存层面的存储,不涉及持久化存储
 *
 * 二、集合框架
 * |-- Collection 接口: 单列集合,用来存储一个个对象
 *   |-- List 接口,存储有序的、可重复的数据 -> 动态数组
 *   |-- ArrayList,LinkedList,Vector
 *   |-- Set 接口,存储无序的、不可重复的数据 -> 数学集合
 *   |-- HashSet,LinkedHashSet,TreeSet
 * |-- Map接口: 双列集合,用来存储一对(key-value)的数据 -> 函数
 *   |-- HashMap,LinkedHashMap,TreeMap,HashTable,Properties
 *
 * 三、Collection 接口方法的使用
 *
 * @author an
 * @create 2021-04-13-14:08
 */
public class CollectionTest {
	@Test
	public void test1() {
		Collection coll = new ArrayList();

		// add()
		coll.add("AA");
		coll.add(123);

		// 获取添加元素个数
		System.out.println(coll.size());

		// addAll(collection) 将集合中的全部元素添加
		Collection coll1 = new ArrayList();
		coll1.addAll(coll);

		// isEmpty() 是否为空
		System.out.println(coll.isEmpty());

		// clear() 清空元素
		coll.clear();
	}

	@Test
	public void test2() {
		Collection coll = new ArrayList();
		coll.add(123);
		coll.add(new String("Tom"));
		coll.add(new Person("Jerry", 20));

		// 判断时会调用 obj 对象所在类的 equals()
		// contains(Object obj): 判断当前集合是否包含obj
		boolean contains = coll.contains(123);
		System.out.println(contains); // true
		System.out.println(coll.contains(new String("Tom")));//true

		Collection coll2 = new ArrayList();

		coll.containsAll(coll2);

	}

	@Test
	public void test3() {

		// remove 从当前集合删除obj元素
		Collection coll = new ArrayList();
		coll.add(123);
		coll.add(134);

		// boolean remove()
		coll.remove(123);
		System.out.println(coll);

		Collection coll2 = Arrays.asList(123, 345);

		// removeAll(Collection coll1): 移除 coll1 的所有元素

		coll2.removeAll(coll);
		// retainAll 求两个集合交集
//        Collection coll2 = Arrays.asList(123,345);
		coll.retainAll(coll2);


		// equals() 判断两个集合是否相等
		coll.equals(coll2);

		// hashcode(): 返回当前对象的哈希值
		System.out.println(coll.hashCode());

		// 集合 -> 数组 toArray()
		Object[] arr = coll.toArray();


		// 数组 -> 集合
		Arrays.asList(arr);


	}

	/**
	 * > iterator 对象称为迭代器,主要用于遍历Collection集合中的元素
	 * > Collection 接口继承了 java.lang.Iterable 接口,该接口有一个
	 * iterator() 方法,所有实现了 Collection 接口的集合类都有一个
	 * iterator() 方法,用来返回一个实现了 iterator 接口的对象
	 * > Iterator 仅用于遍历集合
	 * <p>
	 * > 执行原理
	 * <p>
	 * > remove 方法
	 * <p>
	 * > for-each 遍历 JDK 5.0
	 */


	@Test
	public void test4() {
		// iterator(): 返回 Iterator 接口的实例,用于遍历集合元素
		Collection coll = new ArrayList();
		coll.add(123);
		coll.add(134);

		// 方式一
		Iterator iterator = coll.iterator();
		System.out.println(iterator.next());

		// 越界报异常

		// 方式二,不推荐
		iterator = coll.iterator();
		for (int i = 0; i < coll.size(); i++) {
			System.out.println(iterator.next());
		}

		// 方式三,推荐
		iterator = coll.iterator();
		while (iterator.hasNext()) {
			System.out.println(iterator.next());
		}


		// remove 方法的使用
		iterator = coll.iterator();
		while (iterator.hasNext()) {
			Object obj = iterator.next();
			if ("Tom".equals(obj)) {
				iterator.remove();
			}
		}

		// for-each
		for (Object obj : coll) {
			System.out.println(obj);
		}

	}

	/**
	 * List 接口
	 * 面试题:
	 * ArrayList,LinkedList,Vector三者异同
	 * 同: 三个类都实现了List接口,存储数据的特点相同
	 * 异:
	 * ArrayList: 1.2 作为 List 接口的主要实现类: 线程不安全,效率高: 底层使用Object[] 存储
	 * LinkedList: 1.2 对于频繁插入、删除操作,效率比ArrayList高,底层使用双向链表存储
	 * Vector: 1.0 作为 List 接口的古老实现类: 线程安全,效率低: 底层使用 Object[] 存储
	 *
	 * ArrayList 源码分析:
	 *  JDK 7 情况
	 *  ArrayList list = new ArrayList() 底层创建了长度为 10 的数组
	 *  list.add(123) // elementData[0] = new Integer(123);
	 *  ...
	 *  添加后导致底层element数组容量不够,则扩容
	 *  扩容时,默认扩容为原来的 1.5 倍,同时将原有数组的数据复制到新的数组中
	 *  结论:建议开发中使用带参构造器
	 *
	 *  JDK 8 中
	 *  ArrayList list = new ArrayList() 底层Object[] elementData 初始化为{}
	 *  并没有创建,当我们第一次调用添加操作,底层才创建长度为10的数组,并将123添加
	 *  后续的添加和扩容操作与JDK 7无异
	 *
	 *  小结:JDK 7 中的创建类似饿汉式,JDK 8 中创建类似懒汉式
	 *
	 * LinkedList 源码分析:
	 *   LinkedList list = new LinkedList(); 内部声明Node类型的first和last属性,默认值
	 *   为null.
	 *   其中Node定义为
	 *     private static class Node<E> {
	 *         E item;
	 *         Node<E> next;
	 *         Node<E> prev;
	 *
	 *         Node(Node<E> prev, E element, Node<E> next) {
	 *             this.item = element;
	 *             this.next = next;
	 *             this.prev = prev;
	 *         }
	 *     }
	 *
	 * Vector 源码分析 JDK7 和 JDK8 创建构造器时底层都创建长度为 10,
	 * 扩容时长度变为原来的2倍
	 *
	 *
	 */


	/**
	 * List 接口中的常用方法
	 */

	@Test
	public void test5() {
		ArrayList list = new ArrayList();
		ArrayList list2 = new ArrayList();
		list2.add("CC");
		list.add(123);
		list.add(345);
		list.add("AA");

		System.out.println(list.toString());

		// void add(int index,Object ele): 在index位置插入ele元素
		list.add(1, "BB");

		// void addAll(int index,Collection eles):
		list.add(0, list2);

		// void get(int index) 按照索引来获取
		System.out.println(list.get(1));

		// int indexOf(Object obj) 首次出现的索引位置
		// int lastIndexOf(Object obj)
		list.indexOf(345);

		// void remove(int index);
		list.remove(2);

		// void set(int index,Object ele);
		list.set(1, "aa");

		// list sublist(int fromIndex,int toIndex): 返回从fromIndex到toIndex位置左闭右开
		list.subList(1, 2);

	}


	/**
	 * Set 接口(没有额外定义新的方法,使用的都是Collection中定义过的方法)
	 * 1、HashSet: 主要实现类,线程不安全,可以存储null
	 * 2、LinkedHashSet: HashSet的子类,遍历其内部数据时可以按照添加顺序遍历
	 * 3、TreeSet: 放入的必须是同一个类的对象,使用红黑树存储,排序
	 *
	 * 以 HashSet 为例
	 * 1、无序性: 不等于随机性,存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据
	 * 的 Hash 值
	 *
	 * 2、不可重复性: 保证添加的元素按照equals()判断时,不能返回true
	 *
	 * Set 中添加元素的过程,以HashSet为例
	 * 我们向HashSet中添加a,首先调用a所在类的hashCode方法,计算a的哈希值
	 * 此哈希值通过这种方法计算出在HashSet底层数组中的存放位置,判断数组此位置上是否
	 * 已经有元素,如果此位置上没有其他元素,则元素a添加成功
	 * 如果此位置上有其他元素b(或以链表形式存在多个元素),则比较元素a与元素b的hash值
	 * 如果hash值不相同,则元素a添加成功,如果hash值相同,进而需要调用元素a的equals方法
	 *
	 * LinkedHashSet 在原有的HashSet之上额外添加一个双向链表
	 *
	 * TreeSet 的自然排序
	 *
	 * 1、向 TreeSet 添加的对象,要求是同一个类的对象
	 * 2、向 TreeSet 添加的对象,可以按照从小到大进行排序
	 * 3、自然排序中,我们比较两个对象是否相同的标准为:compareTo方法返回0
	 *
	 *
	 * TreeSet 定制排序
	 */
	@Test
	public void test6() {
		Set set = new HashSet();
		set.add(456);
		set.add(123);
		set.add("aa");
		for (Object st : set) {
			System.out.println(st);
		}
	}

	@Test
	public void test7() {
		Set set = new LinkedHashSet();
		set.add(456);
		set.add(123);
		set.add("aa");
		for (Object st : set) {
			System.out.println(st);
		}
	}

	@Test
	public void test8() {
		Set set = new TreeSet();
		set.add(456);
		set.add(123);
		set.add(25);
		for (Object st : set) {
			System.out.println(st);
		}
		Set set2 = new TreeSet();
		set2.add(new Person("bb", 23));
		set2.add(new Person("aa", 33));
		for (Object st : set2) {
			System.out.println(st);
		}

		Set set3 = new TreeSet(new Comparator() {
			@Override
			public int compare(Object o1, Object o2) {
				Person person1 = (Person) o1;
				Person person2 = (Person) o2;
				return person1.compareTo(person2);

			}
		});
	}

    /**
     * Map 接口
     * 双列数据,存储 key-value 对的数据
     *  |--HashMap:线程不安全,效率高,可以存储null的key和value
     *      |--LinkedHashMap:按照插入顺序遍历,对于频繁的遍历操作,执行效率高于HashMap
     *  |--TreeMap:保证按照添加的key,value进行排序
     *  |--Hashtable:古老的实现类,线程安全,效率低,不能存储null的key和value
     *      |--Properties:常用来处理配置文件,key和value都是String类型
     *
     *  HashMap底层: 数组+链表 (JDK7及之前)
     *               数组+链表+红黑树 (JDK8 及之后)
     *
     * 面试题:
     * 1、HashMap的底层实现原理
     * 2、HashMap和Hashtable的异同
     * 3、CurrentHashMap 与 Hashtable的异同
     *
     * Map 结构理解
     *
     * Map 中的Key: 无序的,不可重复的,用set存储 -> key 要重写 equals 和 hashcode
     * Map 中的value: 无序的,可重复的,使用Collection存储 -> value 所在的类重写equals
     * 一个键值对构成一个Entry,Map中的Entry:无序的,不可重复的,使用Set存储
     *
     * HashMap在JDK7中底层实现原理
     * HashMap map = new HashMap();
     * 底层实例化之后,创建了长度为16的一维数组Entry[] table。
     * ..可能已经执行了多次put..
     * map.put(key1,value1): 首先,调用hashcode()计算key1的哈希值,得到Entry数组的
     * 存放位置,如果此位置上数据为空,此时的 key1-value1 添加成功。
     * 如果此位置上的数据不为空,意味着此位置存在一个或多个数据,比较当前key1和已经存在的一个或
     * 多个数据的hash值,如果hash值与已经存在的hash值都不相同,如果和某一个数据的hash值相同,
     * 继续比较: 调用 key1 所在类的equals() 方法,如果返回false,key1-value1添加成功,否则
     * 使用value1替换相同的value2值
     *
     * 补充:关于情况2和情况3:此时的key1-value1和原来的数据以链表的结构存储
     *
     * 在添加过程中,会涉及到扩容问题,默认扩容方式,扩容为原来容量的2倍,并复制过来
     *
     * JDK8相较于JDK7
     *
     * 1、new HashMap() 底层没有创建一个长度为16的数组
     * 2、JDK8 底层的数组是: Node() 而非 Entry()
     * 3、首次调用 put() 方法时,底层创建长度为16的数组
     * 4、JDK7 底层结构只有数组+链表,JDK8中增加了红黑树
     *    当数组的某一个索引位置上的元素以莲表方式存在的个数>=8,且当前数组的长度>=64,此时改用红黑树存储
     *
     *
     * Map 基本方法:
     *
     *
     *
     */
    @Test
    public void test9() {
        Map map = new HashMap();
        Map map2 = new HashMap();
        // put(k,v) 添加数据
        map.put("2",3);
        map2.put("3",4);
        // putAll()
        map.putAll(map2);
        // Object remove(Object k):移除指定k的key-value对,返回value

        map.remove("2");
//        map.clear();

        // Object get(Object k); 获取对应key对应的value
        map.get("2");

        // boolean containsKey(Object k);
        // boolean containsValue(Object value);
        map.containsKey("2");
        map.containsValue(3);

        // int size();
        // boolean isEmpty();


        // 元视图的操作方法

        // Set keySet(): 返回所有的key组成的Set集合
        // Collection values(): 返回所有value构成的Collection集合
        // Set entrySet(): 返回所有key-value对构成的Set集合



    }
    /**
     * Properties: 处理属性文件, Key 和 Value 都是 String 类型
     *
     * Collections 工具类的使用
     *
     * Collections 是一个操作 Set,List和Map等集合的工具类
     */
    @Test
    public void test10() throws FileNotFoundException {
        ArrayList list = new ArrayList();
        list.add(2); list.add(3); list.add(4);
        ArrayList list2 = new ArrayList();
        // 翻转
        Collections.reverse(list);
        // 随机打乱
        Collections.shuffle(list);
        // 排序
        Collections.sort(list);
        // 交换
        Collections.swap(list,1,2);
        Collections.max(list);
        // 返回出现次数
        Collections.frequency(list,2);
        // copy
        Collections.copy(list,list2);
        // 替换
        Collections.replaceAll(list,2,3);
    }

    public static void main(String[] args) {
        Properties pros = new Properties();
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("jdbc.properties");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        try {
            pros.load(fis);
        } catch (IOException e) {
            e.printStackTrace();
        }
        String name = pros.getProperty("name");
        String password = pros.getProperty("password");
        System.out.println(name+":"+password);
        try {
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

class Person implements Comparable {
	private String name;
	private int age;

	public Person() {
	}

	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person{" +
				"name='" + name + '\'' +
				", age=" + age +
				'}';
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (o == null || getClass() != o.getClass()) return false;
		Person person = (Person) o;
		return age == person.age &&
				Objects.equals(name, person.name);
	}

	@Override
	public int hashCode() {
		return Objects.hash(name, age);
	}

	@Override
	public int compareTo(Object o) {
		if (o instanceof Person) {
			Person person = (Person) o;
			return this.name.compareTo(person.name);
		}
		return 0;
	}
}

泛型

集合容器类在设计/声明阶段不能确定这个容器到底实际存放的是什么类型的对象,所以在JDK1.5之前只能把元素设计为 Object,JDK1.5 之后使用泛型来解决。因为这时候除了元素的类型不确定,其他的部分是确定的。因此把元素的类型设计为一个参数,这个参数称为泛型。

所谓泛型,就是允许在定义类、接口时通过一个标识符表示类中某个属性的类型或者是某个方法的返回值及参数类型,这个类型参数将在使用时(继承或实现这个接口,用这个类型声明变量,创建对象时)确定。

package com.an.java;

import org.junit.Test;

import java.util.*;

/**
 * 一、泛型的使用
 * 在 JDK 1.5 新增
 *
 *
 * 二、自定义泛型结构: 泛型类,泛型接口,泛型方法
 *
 * 1、泛型可能有多个参数,应将它们一起放在尖括号内,用逗号分割。<E1,E2,E3>
 * 2、泛型类的构造器 public TestClass(){}
 * 3、泛型不同的引用不能相互赋值
 * 4、泛型不指定,会被擦除,此时所有泛型类型按照Object处理
 * 5、如果泛型类是一个接口或者抽象类,则不能创建泛型类的对象
 * 6、JDK 1.7 简化操作 ArrayList<Integer> list = new ArrayList<>();
 * 7、泛型中不能使用基本数据类型
 *
 * 三、通配符的使用
 * @author an
 * @create 2021-04-13-20:26
 */
public class GenericTest {

	// 在集合使用泛型之前的情况
	@Test
	public void test() {
		ArrayList list = new ArrayList();

		// 需求:存放学生的成绩
		list.add(78);
		list.add(76);
		list.add(45);

		// 问题一:类型不安全
		list.add("Tom");

		for(Object score:list) {
			// 强转时,有可能出现 ClassCastException
			int studentScore = (Integer) score;
			System.out.println(studentScore);
		}
	}

	// 在集合中使用泛型
	@Test
	public void test2() {
		ArrayList<Integer> list = new ArrayList<Integer>();
		list.add(34);
		list.add(20);
		// 编译时,就会进行类型检查,保证数据的安全

		for(Integer score:list) {
			// 避免了强转操作
			int studentScore = score;
			System.out.println(studentScore);
		}

		Iterator<Integer> iterator = list.iterator();

	}

	// 以 HashMap 为例
	@Test
	public void test3() {
		Map<String,Integer> map = new HashMap<>();
		map.put("2",3);

		// 泛型嵌套
		Set<Map.Entry<String,Integer>> entry = map.entrySet();


	}

	// 泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有关系
	// 泛型方法与所属的类是否是泛型类没有关系
	// 泛型参数是在调用时确定的,而不是在实例化时确定的

	public <E> List<E> copyFromArrayToList(E[] arr) {
		ArrayList<E> list = new ArrayList<>();
		for(E e:arr){
			list.add(e);
		}
		return list;
	}

	/**
	 * 泛型通配符
	 * 通配符 ?
	 * 类 A 是类 B 的父类,G<A> 和 G<B> 没有关系,二者共同的父类 G<?>
	 *
	 * 有限制条件的通配符   <? extends Number> 只允许泛型为 Number 及其 Number 子类的引用
	 * <? super Number> 只允许泛型为 Number 及其 Number 父类的引用
	 * <? extends Comparable> 只允许泛型为 实现 Comparable 接口的实现类的引用调用
	 */
	@Test
	public void test4() {
		List<Object> list1 = null;
		List<String> list2 = null;

		List<?> list = null;

		list = list1;
		list = list2;


	}

	public void show(List<?> list) {
		Iterator<?> iterator = list.iterator();
		while(iterator.hasNext()){
			Object obj = iterator.next();
			System.out.println(obj);
		}
	}



}

/** 自定义泛型类
 * 1、如果定义了泛型类,实例化时没有指定泛型类的类型,则认为此泛型类为Object类型
 * 2、如果大家定义了类是泛型的,建议在实例化时指定类的类型
 * 3、泛型类的继承
 */
class Order<T> {
	T orderName;
	T orderId;

	// 类的内部接口就可以使用类的泛型

	public Order(){}

	public Order(T orderName,T orderId){
		this.orderName = orderName;
		this.orderId = orderId;
	}

	public T getOrderName() {
		return orderName;
	}


}

class subOrder extends Order<Integer> {

}

// 仍然是泛型类
class subOrder2<T> extends Order<T> {

}

IO 流

File

java.io.File 类:文件和文件目录路径的抽象表示形式,与平台无关。

File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。如果需要访问文件内容本身,需要使用输入/输出流。

想要在 Java 程序中表示一个真实存在的文件或目录,那么必须要有一个 File 对象,但是 Java 程序中的一个 File 对象,可能没有一个真实存在的文件或目录。

File 对象可以作为参数传递给流的构造器。

package com.an.java;

import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.util.Date;

/**
 * File 类的使用
 * 1、File 类的对象代表一个文件或文件目录
 * 2、声明在 java.io 下
 * 3、后续 File 对象的使用常常作为参数传递到流的构造器中
 *
 * @author an
 * @create 2021-04-13-21:15
 */
public class FileTest {
	/*
	1、如何创建 File 类的实例

	2、相对路径:相较于某个路径下指明的路径
	   绝对路径:包含盘符在内的路径

	3、路径分隔符



	 */
	@Test
	public void test1() {

		// 构造器 1
		File file = new File("hello.txt"); // 相对于当前module
		System.out.println(file);

		// 构造器 2
		File file2 = new File("D:/","Hello");
		System.out.println(file2);

		// 构造器 3
		File file3 = new File(file2,"h1.txt");
		System.out.println(file3);

		// 获取绝对路径
		String path = file.getAbsolutePath();

		// 获取路径
		String path2 = file.getPath();

		// 获取名称
		String name = file.getName();

		// 获取上层文件目录路径
		String path3 = file.getParent();

		// 获取文件长度(字节数)
		long len = file.length();
		System.out.println(len);

		// 获取最后一次修改时间,时间戳
		long last = file.lastModified();
		System.out.println(new Date(last));
		// 获取指定目录下所有文件或者文件目录的名称数组
		// String[] list()

		// File[] listFiles() 获取 File 数组

		// 是文件还是文件目录

		Boolean isDir = file.isDirectory();

		Boolean isFile = file.isFile();

		// 是否存在
		Boolean isExist = file.exists();

		// 是否可读,可写,隐藏
		Boolean isRead = file.canRead();
		Boolean isWrite = file.canWrite();
		Boolean isHid = file.isHidden();


		// 创建硬盘中对应的文件和文件目录
		File file4 = new File("a.txt");
		try {
			file4.createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
		}

		// 删除
		file4.delete();

		// 文件目录的创建
		File file5 = new File("an");
		file5.mkdir();
		// 如果上层目录不存在,一并创建
		file.mkdirs();
	}

}

IO 流原理及分类

I/O 是 Input/Output 的缩写,I/O 技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等。

Java 程序中,对于数据的输入/输出操作以"流(Stream)"的方式进行。

java.io 包下提供了各种"流"的类和接口,用来获得不同种类的数据,并通过标准的方法输入或输出流。

按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)。

按数据流的流向不同分为:输入流,输出流。

按流的角色的不同分为:节点流,处理流。

抽象基类 字节流 字符流
输入流 InputStream Reader
输出流 OutputSteam Writer

1、Java 的 IO 流共涉及 40 多个类,实际上非常规则,都是从如下 4 个抽象类中派生出来的。

2、由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。

FileReader/FileWriter

package com.an.java;

import org.junit.Test;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/**
 * 一、流的体系结构
 *
 * 抽象基类         节点流               缓冲流(处理流的一种)
 * InputStream     FileInputStream     BufferedInputStream
 * OutputStream    FileOutputStream    BufferedOutputStream
 * Reader          FileReader          BufferedReader
 * Writer          FileWriter          BufferedWriter
 *
 * @author an
 * @create 2021-04-13-21:56
 */
public class FileReaderWriterTest {
	@Test
	/**
	 * 将 hello.txt 内容读入程序,并输出到控制台
	 *
	 * 1、read() 的理解: 返回读入的一个字符,如果达到文件末尾,返回 -1
	 * 2、异常的处理:为了保证流资源一定可以执行关闭操作,需要使用 try-catch-finally 处理
	 * 3、读入的文件一定要存在,否则会报 FileNotFoundException
	 */
	public void test1() {
		// 相较于当前Module ,如果在 main 方法则是相对于当前工程

		// 1、实例化 File 类的对象,指明要操作的文件
		File file = new File("hello.txt");

		// 2、提供具体的流
		FileReader fr = null;
		try {
			fr = new FileReader(file);

			// 3、数据的读入
			// read() 返回读入的一个字符,如果到达文件末尾,返回-1
			//		int data = fr.read();
			//		while(data != -1) {
			//			System.out.print((char)data);
			//			data = fr.read();
			//		}

			// 方式二:
			int data;
			while ((data = fr.read()) != -1) {
				System.out.print((char) data);
			}
		}catch (IOException e){
			e.printStackTrace();
		}finally {
			// 4、流的关闭操作
			try {
				if(fr != null)
					fr.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}


	}

	// 对 read() 操作升级:使用 read 的重载方法
	@Test
	public void test2() {
		// 1、File 类的实例化
		File file = new File("hello.txt");

		// 2、FileReader 流的实例化
		FileReader fr = null;
		try {
			fr = new FileReader(file);

			// 3、读入的操作
			// read(char[] cbuf): 返回每次读入到数组中的字符个数,如果读到末尾返回 -1
			char[] cbuf = new char[5];
			int len;
			while ((len = fr.read(cbuf)) != -1) {
				// 错误的写法
//				for (int i = 0; i < cbuf.length; i++) {
//					System.out.print(cbuf[i]);
//				}
				for (int i = 0; i < len; i++) {
					System.out.print(cbuf[i]);
				}
				// 另一种写法
				// String str = new String(cbuf,0,len);
			}
		}catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(fr!=null) {
				try {
					fr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		// 4、资源的关闭


	}

	/**
	 * 从内存中写出数据到硬盘的文件中
	 *
	 * 说明:
	 * 1、输出操作,对应的File可以不存在,如果不存在,输出过程中,会自动创建
	 * 如果存在,则根据第二个参数表明是否对原有文件的覆盖。
	 *
	 */

	@Test
	public void test3() throws IOException {
		// 1、提供File类的对象,指明写出的文件
		File file = new File("hello1.txt");

		// 2、提供FileWriter的对象,用于数据的写出
		FileWriter fw = new FileWriter(file,false);

		// 3、写出操作

		fw.write("I have a dream!\n");
		fw.write("You need to have a dream!\n");

		// 4、流资源的关闭

		fw.close();

	}

	/**
	 * 读进来再写出去
	 * 不能用字符流处理图片等字节数据
	 */

	@Test
	public void test4() {
		// 1、创建 File 类的对象,指明读入和写出文件
		File srcFile = new File("hello.txt");
		File destFile = new File("hello1.txt");

		// 2、创建输入流和输出流
		FileReader fr = null;
		FileWriter fw = null;
		try {
			fr = new FileReader(srcFile);
			fw = new FileWriter(destFile);
			// 3、数据的读入和写出
			char[] cbuf = new char[5];
			int len;
			while((len = fr.read(cbuf))!=-1) {
				fw.write(cbuf, 0,len);
			}
		}catch (IOException e) {
			e.printStackTrace();
		}finally {
			// 4、关闭流资源
			try {
				if(fw!=null)
				fw.close();
			}catch (IOException e){
				e.printStackTrace();
			}
			try {
				if(fr!=null)
					fr.close();
			}catch (IOException e){
				e.printStackTrace();
			}
		}
	}
}

FileInputStream/FileOutputStream

package com.an.java;

import org.junit.Test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 结论:
 * 对于文本文件(.txt,.java,.c...),使用字符流处理
 * 对于非文本文件(.png,.jpg,.mp4...),使用字节流处理
 * @author an
 * @create 2021-04-14-10:14
 */
public class FileInputOutputStreamTest {
	@Test
	public void test1() {
		// 1、造文件
		File file = new File("hello.txt");

		// 2、造流
		FileInputStream fis = null;

		try {
			fis = new FileInputStream(file);

			// 3、读数据

			byte[] buffer = new byte[5];

			int len;
			while((len = fis.read(buffer))!=-1) {
				String str = new String(buffer,0,len);
				System.out.print(str);
			}

			// 4、关闭流

		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(fis != null) {
				try {
					fis.close();
				}
				catch (IOException e){
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 实现对图片的复制操作
	 */
	@Test
	public void test2() {

		File srcFile = new File("test.jpg");
		File destFile = new File("test2.jpg");
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			fis = new FileInputStream(srcFile);
			fos = new FileOutputStream(destFile);

			byte[] buffer = new byte[5];
			int len;
			while((len=fis.read(buffer))!=-1) {
				fos.write(buffer,0,len);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(fis != null) {
				try{
					fis.close();
				}catch (IOException e){
					e.printStackTrace();
				}
			}
			if(fos != null) {
				try{
					fos.close();
				}catch (IOException e){
					e.printStackTrace();
				}
			}
		}


	}

	// 指定路径下文件的复制
	public static void copyFile(String srcPath,String destPath) {
		File srcFile = new File(srcPath);
		File destFile = new File(destPath);
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			fis = new FileInputStream(srcFile);
			fos = new FileOutputStream(destFile);

			byte[] buffer = new byte[1024];
			int len;
			while((len=fis.read(buffer))!=-1) {
				fos.write(buffer,0,len);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(fis != null) {
				try{
					fis.close();
				}catch (IOException e){
					e.printStackTrace();
				}
			}
			if(fos != null) {
				try{
					fos.close();
				}catch (IOException e){
					e.printStackTrace();
				}
			}
		}
	}

}

缓冲流(以字节流为例)

package com.an.java;

import org.junit.Test;

import java.io.*;

/**
 * 处理流之一: 缓冲流
 * 1、缓冲流
 * BufferedInputStream
 * BufferedOutputStream
 * BufferedReader
 * BufferedWriter
 *
 * @author an
 * @create 2021-04-14-10:31
 */
public class BufferedTest {
	/**
	 * 实现非文本文件的复制
	 */

	@Test
	public void BufferedStreamTest() {
		// 1 造文件
		File srcFile = new File("test.jpg");
		File destFile = new File("test3.jpg");

		// 2 造流

		FileInputStream fis = null;
		FileOutputStream fos = null;
		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		try {
			// 2.1 造节点流
			fis = new FileInputStream(srcFile);
			fos = new FileOutputStream(destFile);
			// 2.2 造缓冲流
			bis = new BufferedInputStream(fis);
			bos = new BufferedOutputStream(fos);


			// 3 复制
			byte[] buffer = new byte[10];
			int len;
			while ((len = bis.read(buffer)) != -1) {
				bos.write(buffer, 0, len);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {

			// 4 先关闭外层流,再关闭内层流
			// 其实只需要考虑关闭外层,在关闭外层流的同时,内层也同时关闭
			if (bos != null) {
				try {
					bos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (bis != null) {
				try {
					bis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

		}
	}
}

转换流

package com.an.java;

import org.junit.Test;

import java.io.*;

/**
 * 处理流之二: 转换流的使用
 * 1、转换流: 属于字符流,处理流
 * InputStreamReader
 * OutputStreamWriter
 *
 * 2、作用: 提供字节流和字符流之间的转换
 *
 * 3、解码: 字节、字节数组 -> 字符数组、字符串
 *    编码: ...
 *
 * 4、字符集
 *    ASCII: 用一个字节的7位表示
 *    ISO8859-1: 拉丁码表,用一个字节的8位表示
 *    GB2312: 中国的中文编码表,最多两个字节编码所有字符
 *    GBK:中文编码表升级,最多两个字节编码
 *    Unicode: 国际标准码
 *    UTF-8: 变长的编码方式,可用 1-4 个字节表示一个字符
 *
 * @author an
 * @create 2021-04-14-10:44
 */
public class InputStreamReaderTest {

	@Test
	public void test1() {
		FileInputStream fis = null;
		InputStreamReader isr = null;

		try {
			fis = new FileInputStream("hello.txt");

			// 参数 2 指明了字符集,具体使用那个字符集,取决于文件保存时使用的字符集
			isr = new InputStreamReader(fis,"UTF-8");

			char[] cbuf = new char[2];
			int len;
			while((len = isr.read(cbuf)) != -1) {
				String str = new String(cbuf, 0, len);
				System.out.print(str);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(isr != null){
				try{
					isr.close();
				}catch (IOException e){
					e.printStackTrace();
				}
			}
		}


	}


	/**
	 * 综合使用 InputStreamReader 和 OutputStreamWriter
	 */
	@Test
	public void test2() {
		FileInputStream fis = null;
		FileOutputStream fos = null;
		InputStreamReader isr = null;
		OutputStreamWriter osw = null;

		File file = new File("hello.txt");
		File file2 = new File("hello2.txt");

		try {
			fis = new FileInputStream(file);
			fos = new FileOutputStream(file2);


			// 参数 2 指明了字符集,具体使用那个字符集,取决于文件保存时使用的字符集
			isr = new InputStreamReader(fis,"UTF-8");
			osw = new OutputStreamWriter(fos,"gbk");

			char[] cbuf = new char[2];
			int len;
			while((len = isr.read(cbuf)) != -1) {
				osw.write(cbuf,0,len);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(isr != null){
				try{
					isr.close();
				}catch (IOException e){
					e.printStackTrace();
				}
			}
			if(osw != null){
				try{
					osw.close();
				}catch (IOException e){
					e.printStackTrace();
				}
			}
		}
	}
}

标准输入输出流、打印流、数据流

System.inSystem.out 分别代表了系统标准的输入和输出设备。

默认输入设备是:键盘,输出设备是:显示器

System.in 的类型是 InputStream

System.out 的类型是 PrintStream,是 OutputStream 的子类 FilterOutputStream 的子类

重定向: 通过 System 类的 setInsetOut 方法对默认设备进行改变

package com.an.java;

import org.junit.Test;

import java.io.*;

/**
 * 其他流的使用
 * 1、标准输入输出流
 * 2、打印流
 * 3、数据流
 *
 * @author an
 * @create 2021-04-14-15:11
 */
public class OtherStreamTest {
	/*
	1 标准的输入、输出流

	1.1
	System.in: 标准输入流,默认从键盘输入
	System.out: 标准输出流,默认输出到屏幕

	1.2
	System 类的 setIn(InputStream)/setOut(PrintStream) 方法重新指定输入输出流

	1.3
	练习:从键盘输入字符串,要求将读取到的整行字符串转换成大写输出,然后继续进行输入操作
	直到当输入"e"或者"exit"时,退出程序。

	方法一:采用Scanner, 调用next()返回一个字符串
	方法二:采用System.in -> 转换流 -> BufferedReader 的 Readline
	 */
	public static void main(String[] args) {
		BufferedReader br = null;
		try {
			InputStreamReader isr = new InputStreamReader(System.in);
			br = new BufferedReader(isr);

			while (true) {
				String data = br.readLine();
				if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)) {
					System.out.println("程序结束");
					break;
				}
				String upperCase = data.toUpperCase();
				System.out.println(upperCase);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/*
	打印流: 实现将基本数据类型的数据格式转换为字符串输出
	打印流: PrintStream 和 PrintWriter
	> 提供了一系列重载的 print 和 println 方法,用于多种数据类型的输出
	> PrintStream 和 PrintWriter 不会抛出 IOException 异常
	> PrintStream 和 PrintWriter 有自动 flush 功能
	> PrintStream 打印的所有字符都使用平台的默认字符编码转换为字符串
	> System.out 返回的是 PrintStream 的实例
	 */


	/*
	数据流
	DataInputStream DataOutputStream
	为了方便操作Java语言的基本数据类型和String的数据,可以使用数据流
	用于读取和写出基本数据类型变量和字符串
	将内存中的基本类型变量写入到文件中
	 */
	@Test
	public void test3() throws Exception{
		// 1、
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));

		// 2、
		dos.writeUTF("test");
		dos.flush(); // 刷新操作,一旦执行就将已有数据写入
		dos.writeInt(23);
		dos.flush();
		dos.writeBoolean(true);
		dos.flush();

		// 3、
		dos.close();
	}

	/*
	将文件中存取的基本输出类型读入内存中
	读取的顺序要和写入的顺序一致
	 */


	@Test
	public void test4() throws Exception{
		// 1、
		DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
		// 2、
		String name = dis.readUTF();
		int age = dis.readInt();
		boolean isMale = dis.readBoolean();

		System.out.println(name + ":" + age + ":" + isMale);
		// 3、
	}
}


对象流

ObjectInputStreamObjectOutputStream

用于存储和读取基本数据类型或对象的处理流,它的强大之处在于可以把对象写入数据源中,也能把对象从数据源中还原回来。

  • 序列化: 用 ObjectOutputStream 类保存基本数据类型或对象的机制

  • 反序列化: 用 ObjectInputStream 类读取基本类型数据或对象的机制

ObjectOutputStream 和 ObjectInputStream 不能序列化 static 和 transient 修饰的成员变量

对象的序列化机制允许把内存中的 Java 对象转换成平台无关的二进制流,从而允许把这种二进制流持久化地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其他程序获取了这种二进制流,就可以恢复成原来的Java 对象。

序列化的好处在于可将任何实现了 Serializable 接口的对象转换成字节数据,使其在保存和传输时可被还原。

序列化是 RMI 远程方法调用过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础,因此序列化是JavaEE 平台的基础。

如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。

  • Serializable

  • Externalizable

package com.an.java;

import org.junit.Test;

import java.io.*;

/**
 * 对象流的使用
 * 1、ObjectInputStream 和 ObjectOutputStream
 * 2、作用: ....
 * 3、要想一个 Java 对象是可序列化的,需要满足要求
 *
 * @author an
 * @create 2021-04-14-15:55
 */
public class ObjectInputOutputStreamTest {
	/*
	序列化:将内存中的 Java 对象保存到磁盘中传输出去
	使用 ObjectOutputStream 输出
	 */
	@Test
	public void testObjectOutputStream() {
		ObjectOutputStream oos = null;
		try {
			oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
			oos.writeObject(new Person("小张",23));

			oos.flush();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (oos != null) {
				try {
					oos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}


	}

	/*
	反序列化: 将磁盘文件中的对象还原成内存中的Java对象
	 */

	@Test
	public void testObjectInputStream() {
		ObjectInputStream ois = null;
		try {
			ois = new ObjectInputStream(new FileInputStream("object.dat"));

			Object obj = ois.readObject();
			Person str = (Person) obj;
			System.out.println(str);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			if (ois != null) {
				try {
					ois.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}


}

/**
 * Person 类需要满足如下的要求即可序列化
 * 1、实现接口: Serializable
 * 2、需要当前类提供一个全局常量 serialVersionUID
 * 3、Person 类的内部所有属性也必须是可序列化的
 *
 * 凡是实现 Serializable 接口的类都有一个表示序列化版本标识符的静态变量:
 * > private static final long serialVersionUID;
 * > serialVersionUID 用于表明类的不同版本间的兼容性,简言之,其目的是以序列化对象进行版本
 * 控制,有关各版本反序列化时是否兼容
 * > 如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的,若
 * 类的实例变量做了修改,serialVersionUID 可能发生变化,建议显示声明
 *
 * 简单来说,Java 的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。
 * 在进行反序列化时,JVM 会把传来的字节流中的 serialVersionUID 与本地相应实体类的
 * serialVersionUID 进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现
 * 序列化版本不一致的异常。
 */

class Person implements Serializable{

	public static final long serialVersionUID = 2348325398439L;

	private String name;
	private int age;


	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person{" +
				"name='" + name + '\'' +
				", age=" + age +
				'}';
	}
}

随机存取文件流

RandomAccessFile 声明在 java.io 包下,但直接继承于 java.lang.Object 类,并且它实现了 DataInput、DataOutput 这两个接口,也就意味着这个类既可以读也可以写。

RandomAccessFile 类支持随机访问的方式,程序可以直接跳到文件的任意地方来读、写文件:

  • 支持只访问文件的部分内容。
  • 可以向已存在的文件后追加内容。

RandomAccessFile 对象包含一个记录指针,用来标示当前读写处的位置。

RandomAccessFile 类对象可以自由移动记录指针

  • long getFilePointer(): 获取文件记录指针的当前位置
  • void seek(long pos): 将文件记录指针定位到 pos 位置
package com.an.java;

import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;

/**
 * 1、RandomAccessFile 直接继承于java.lang.Object类,实现了DataInput和DataOutput接口
 * 2、既可以作为一个输入流,又可以作为一个输出流
 * 3、如果RandomAccessFile作为输出流存在,如果文件不存在,则自动创建
 * 4、可以通过相关操作,实现插入操作
 * 如果文件存在,则会对原有文件内容覆盖,默认从头覆盖
 * @author an
 * @create 2021-04-15-10:28
 */
public class RandomAccessFileTest {

	/**
	 * RandomAccessFile的访问模式
	 * r: 以只读的方式打开
	 * rw: 打开以便读取和写入
	 * rwd: 打开以便读取和写入;同步文件内容的更新
	 * rws: 打开以便读取和吸入;同步文件内容和元数据的更新
	 */
	@Test
	public void test1()  {
		RandomAccessFile raf1 = null;
		RandomAccessFile raf2 = null;
		try {
			raf1 = new RandomAccessFile(new File("test.jpg"),"r");
			raf2 = new RandomAccessFile(new File("testx.jpg"),"rw");

			byte[] buffer = new byte[1024];
			int len;
			while((len = raf1.read(buffer))!= -1){
				raf2.write(buffer,0,len);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(raf1 != null) {
				try {
					raf1.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(raf2 != null) {
				try {
					raf2.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}


	}

	@Test
	public void test2() throws IOException {
		RandomAccessFile raf1 = new RandomAccessFile("hello.txt","rw");
//		raf1.write("xyz".getBytes());

		raf1.seek(3); // 指针调到角标为3的位置
		raf1.write("xyz".getBytes());

		raf1.close();
	}

	/**
	 * 使用RandomAccessFile 实现插入效果
	 */
	@Test
	public void test3() throws IOException {
		RandomAccessFile raf1 = new RandomAccessFile("hello.txt","rw");

		raf1.seek(3);
		// 保存指针3后面的所有数据到StringBuilder中
		StringBuilder builder = new StringBuilder((int) new File("hello.txt").length());
		byte[] buffer = new byte[20];
		int len;
		while((len=raf1.read(buffer))!=-1) {
			builder.append(new String(buffer,0,len));
		}
		// 调回指针,写入xyz
		raf1.seek(3);
		raf1.write("xyz".getBytes());

		// 将 StringBuilder 中的数据写入
		raf1.write(builder.toString().getBytes());
		raf1.close();

	}
}

NIO

Java NIO 是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的Java IO API。NIO 与原来的 IO 有同样的作用和目的,但是使用的方式完全不同,NIO 支持面向缓冲区的(IO是面向流的)、基于通信的IO操作。NIO 以更加高效的方式进行文件的读写操作。

Java API 中提供了两套 NIO,一套是标准输入输出NIO,另一套是网络编程NIO。

NIO 2: JDK7 中引入的 NIO 升级版。

网络编程

ip

package com.an.java;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * 一、网络编程需要解决的两个主要的问题
 * 1、如何准确地定位网络上一台或多台主机: 定位网络上的特定的应用
 * 2、找到主机后如何可靠高效地进行数据传输
 *
 * 二、网络编程中的两个要素
 * 1、对应问题一: IP 和端口号
 * 2、对应问题二:提供网络通讯协议: TCP/IP 网络参考模型
 *
 *
 * 三、通信要素一: IP 和端口号
 * 1、IP: 唯一的标识 Internet 上的计算机(通信实体)
 * 2、在 Java 中使用InetAddress类代表IP
 * 3、IP分类: IPv4 和 IPv6; 万维网 和 局域网
 * 4、域名: www.baidu.com
 * 5、本地回路地址: 127.0.0.1 对应着 localhost
 * 6、如何实例化 InetAddresss: 两个方法 getByName, getLocalHost
 *      两个方法: getHostName getHostAddress
 * 7、端口号: 正在计算机上运行的进程
 * 要求: 不同的进程有不同的端口号
 * 范围: 被规定为一个16位的整数 0~65535
 * 8、端口号与ip地址组合得出一个网络套接字: Socket
 *
 * @author an
 * @create 2021-04-15-11:00
 */
public class InetAddressTest {
	public static void main(String[] args) {
		try {
			InetAddress inet1 = InetAddress.getByName("192.168.10.14");
			System.out.println(inet1);

			InetAddress inet2 = InetAddress.getByName("www.baidu.com");
			System.out.println(inet2);

			InetAddress inet3 = InetAddress.getLocalHost();
			System.out.println(inet3);

			InetAddress inet4 = InetAddress.getByName("127.0.0.1");

			System.out.println(inet4.getHostName());
			System.out.println(inet4.getHostAddress());
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
	}
}

TCP

package com.an.java;

import org.junit.Test;

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 实现TCP的网络编程
 * 例子1:客户端发送数据给服务端,服务端将数据显示在控制台上
 * @author an
 * @create 2021-04-15-16:35
 */
public class TCPTest1 {

	// 客户端
	@Test
	public void client() {
		Socket socket = null;
		OutputStream os = null;
		try {
			// 1、创建Socket对象,指明服务器端的ip和端口号
			InetAddress inet = InetAddress.getByName("192.168.233.1");
			socket = new Socket(inet, 8888);
			// 2、获取一个输出流用于输出数据
			os = socket.getOutputStream();
			// 3、写出数据
			os.write("你好".getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 4、关闭
			if(os!=null)
				try {
					os.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			if(socket!=null)
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}

	}


	// 服务端
	@Test
	public void server() {
		ServerSocket ss = null;
		Socket socket = null;
		InputStream is = null;
		ByteArrayOutputStream baos = null;
		try {
			// 1、创建服务器端的Socket, 指明自己的端口号
			ss = new ServerSocket(8888);
			// 2、调用accept方法接收来自客户端的socket
			socket = ss.accept();
			// 3、获取输出流
			is = socket.getInputStream();
			// 4、读取输入流的数据
			baos = new ByteArrayOutputStream();


			byte[] buffer = new byte[20];
			int len;
			while((len=is.read(buffer))!=-1) {
				baos.write(buffer,0,len);
			}

			System.out.println(baos.toString());
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 5、关闭
			if(baos != null) {
				try {
					baos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(is!=null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(socket!=null) {
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(ss!=null) {
				try {
					ss.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}
}

package com.an.java;

import org.junit.Test;

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 例题2: 客户端发送文件给服务端,服务端将文件保存在本地
 *
 * @author an
 * @create 2021-04-15-17:11
 */
public class TCPTest2 {

	
	@Test
	public void client() {
		Socket socket = null;
		OutputStream os = null;
		FileInputStream fis = null;
		try {
			socket = new Socket(InetAddress.getByName("127.0.0.1"), 9090);
			os = socket.getOutputStream();

			fis = new FileInputStream(new File("test.jpg"));
			byte[] buffer = new byte[1024];
			int len;
			while ((len = fis.read(buffer)) != -1) {
				os.write(buffer, 0, len);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (os != null) {
				try {
					os.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (socket != null) {
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}


	@Test
	public void server() {
		Socket socket = null;
		InputStream is = null;
		FileOutputStream fos = null;
		try {
			ServerSocket ss = new ServerSocket(9090);
			socket = ss.accept();
			is = socket.getInputStream();
			fos = new FileOutputStream(new File("new.jpg"));

			byte[] buffer = new byte[1024];
			int len;
			while ((len = is.read(buffer)) != -1) {
				fos.write(buffer, 0, len);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (socket != null) {
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}

}

package com.an.java;

import org.junit.Test;

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 例题3:从客户端发送文件到服务端,服务端将文件保存到本地,并发送成功给客户端
 * @author an
 * @create 2021-04-15-17:20
 */
public class TCPTest3 {
	@Test
	public void client() {
		Socket socket = null;
		OutputStream os = null;
		FileInputStream fis = null;
		InputStream is = null;
		ByteArrayOutputStream baos = null;
		try {
			socket = new Socket(InetAddress.getByName("127.0.0.1"), 9090);
			os = socket.getOutputStream();

			fis = new FileInputStream(new File("test.jpg"));
			byte[] buffer = new byte[1024];
			int len;
			while ((len = fis.read(buffer)) != -1) {
				os.write(buffer, 0, len);
			}
			socket.shutdownOutput();

			is = socket.getInputStream();

			baos = new ByteArrayOutputStream();

			byte[] buffer1 = new byte[20];

			while((len = is.read(buffer1))!=-1) {
				baos.write(buffer1,0,len);
			}
			System.out.println(baos.toString());
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (os != null) {
				try {
					os.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (socket != null) {
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(is != null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(baos != null) {
				try {
					baos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}


	@Test
	public void server() {
		Socket socket = null;
		InputStream is = null;
		FileOutputStream fos = null;
		try {
			ServerSocket ss = new ServerSocket(9090);
			socket = ss.accept();
			is = socket.getInputStream();
			fos = new FileOutputStream(new File("new1.jpg"));

			byte[] buffer = new byte[1024];
			int len;
			while ((len = is.read(buffer)) != -1) {
				fos.write(buffer, 0, len);
			}
			OutputStream outputStream = socket.getOutputStream();
			outputStream.write("接收成功".getBytes());

		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (socket != null) {
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}
}

UDP 网络通信

package com.an.java;

import org.junit.Test;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * UDP 协议的网络编程
 * @author an
 * @create 2021-04-15-19:12
 */
public class UDPTest {

	@Test
	public void sender() throws IOException {
		DatagramSocket socket = new DatagramSocket();

		String str = "我是UDP方式发送的";
		byte[] data = str.getBytes();
		InetAddress inet = InetAddress.getLocalHost();
		DatagramPacket packet = new DatagramPacket(data,0,data.length,inet,8088);
		socket.send(packet);
		socket.close();
	}

	@Test
	public void receiver() throws IOException {
		DatagramSocket socket = new DatagramSocket(8088);

		byte[] buffer = new byte[100];
		DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);

		socket.receive(packet);

		System.out.println(new String(packet.getData(),0,packet.getLength()));

		socket.close();
	}
}

URL 类

package com.an.java;

import org.junit.Test;

import java.net.MalformedURLException;
import java.net.URL;

/**
 * URL 网络编程
 * 1、URL: 统一资源定位符,对应着互联网的某一资源地址
 * 2、格式
 *    协议 主机名 端口号 资源地址 参数列表
 * @author an
 * @create 2021-04-15-19:21
 */
public class URLTest {
	@Test
	public static void main(String[] args) {
		try {
			URL url = new URL("https://www.bilibili.com/video/BV1Kb411W75N?p=629&spm_id_from=pageDriver");
			// 协议名
			System.out.println(url.getProtocol());
			// 主机名
			System.out.println(url.getHost());
			// 端口号
			System.out.println(url.getPort());
			// 文件路径
			System.out.println(url.getPath());
			// 文件名
			System.out.println(url.getFile());
			// 查询名
			System.out.println(url.getQuery());

		} catch (MalformedURLException e) {
			e.printStackTrace();
		}

	}
}

posted @ 2021-04-13 22:37  ans20xx  阅读(69)  评论(0编辑  收藏  举报