一、泛型
(一)泛型的概述
1、泛型:广泛的类型,在定义类的时候,某些方法的参数列表或者返回值类型不确定的时候,就是用一个符号来表示那些尚未确定的类型
2、格式
ArrayList<String> list = new ArrayList<>();
3、泛型的好处
(1)提高了数据的安全性,将运行期的问题提前到编译期
(2)避免了强转的麻烦
4、注意事项
(1)泛型必须书写为引用数据类型
(2)泛型书写的时候,前后必须保持一致
(3)JDK7以后赋值符号右侧的泛型可以省略
(二)泛型类
1、概述:带泛型的类
2、格式
class 类名<泛型1, 泛型2, 泛型3...> { }
3、说明
(1)类后面跟着的泛型类型是泛型的声明,一旦泛型声明出来,就相当于未来会有一个已知类型,这个类型就可以在类中使用
(2)泛型类型的声明:只要是一个合法标识符即可,一般使用单个的大写字母表示:T(Type)、E(Element)、K(Key)、V(Value)
package com.hqyj.demos;
public class Demo02_Class {
public static void main(String[] args) {
MyClass<String> mc = new MyClass<>();
MyClass<Integer> mc2 = new MyClass<>();
mc.test01("qwe");
System.out.println(mc.test02());
}
}
class MyClass<T> {
public void test01(T t) {
System.out.println(t);
}
public T test02() {
return null;
}
}
(三)泛型方法
1、在方法的声明上,带上泛型,就是泛型方法
2、格式
修饰符 <泛型1, 泛型2, 泛型3...> 返回值类型 方法名(参数列表) { }
3、注意事项:
(1)在方法上声明泛型,可以在整个方法中使用,出了这个方法就没有用了
(2)如果【非静态方法】没有声明任何泛型,可以直接使用类的泛型,此时泛型类型的确定与创建对象时的泛型类型保持一致即可;如果【非静态方法】自己定了泛型,泛型就会随着参数的类型进行变化
(3)【静态方法】不能使用类的泛型,只能自己定义泛型,然后在当前静态方法中进行使用
package com.hqyj.demos;
import com.hqyj.homework.Student;
public class Demo03_Method {
public static void main(String[] args) {
MyClass01<String> mc01 = new MyClass01<>();
mc01.test03("qwe");
mc01.test03(123);
mc01.test05("qwe", "asd");
mc01.test05(123, "asd");
MyClass01.test06(new Student());
}
}
class MyClass01<T> {
//1、方法的参数列表为泛型
public void test01(T t) {
}
//2、方法的返回值类型为泛型
public T test02() {
return null;
}
//方法自己定义泛型
public <E> void test03(E e) {
}
public <E> E test04() {
return null;
}
public <E> void test05(E e, T t) {
//方法使用自己泛型的同时还可以使用类的泛型
}
//静态方法使用泛型
//静态方法只能使用自己的泛型
/**
* 泛型类随着对象的创建而确定泛型,静态方法优先于对象存在
* 如果静态方法使用类的泛型,对象就极有可能不存在,泛型类型就不能确定
* 所以静态方法要使用泛型就需要自己定义
*/
public static <E> void test06(E e) {
}
public static <E> E test07() {
return null;
}
}
(五)泛型接口
1、概述:带泛型的接口
2、格式
interface 接口名<泛型1, 泛型2, 泛型3...> { }
3、说明
(1)在接口的声明上,定义好泛型,整个接口中都可以使用该泛型
(2)泛型接口被其它类实现
-
类实现接口的时候,接口的泛型确定为具体类型
class 类名 implements 接口名<具体类型> { 实现类实现接口的方法时方法确定为具体类型; }
-
类实现接口的时候,接口的泛型尚未确定
class 类名 implements 接口名<泛型1, 泛型2, 泛型3...> { 实现类实现接口的方法时方法的类型还尚未确定; }
(2)字符流:
(1)FileInputStream(File file):将一个文件的路径封装为File对象封装在FileInputStream的存储列表中,创建一个可以用来读取字节信息的流对象
(2)FileInputStream(String name):将一个文件的路径封装为字符串封装在FileInputStream的存储列表中,创建一个可以用来读取字节信息的流对象
字节流
(一)概述
1、可以直接操作字节信息的流对象
2、字节输入流的顶层抽象父类:InputStream
3、字节输出流的顶层抽象父类:OutputStream
4、根据交互设备的不同,具有不同的子实现类
(二)InputStream
1、常用方法:
(1)close():关闭资源
(2)read():从当前的字节流中读取一个字节信息并且返回,到达文件末尾返回-1
(3)read(byte[] b):从输入流中读取一定数量的字节信息,并将其存储在字节数组中,到达文件末尾返回-1
(4)available():返回流中剩余的字节个数
2、InputStream是一个抽象类,不能创建对象,根据子实现类的不同,具有不同的功能那个
(三)FileInputStream
1、是InputStream的子实现类,用于和磁盘上的文件进行交互
2、FileInputStream不仅可以一次读取一个字节信息,还能够一次读取多个字节信息,不仅能够操作纯文本文件,还能够操作图片、音频、视频等等格式的文件
3、构造方法:
-
类实现接口的时候,接口的泛型尚未确定;类还有自己的泛型
class 类名<泛型1, 泛型2, 泛型3...> implements 接口名<泛型1, 泛型2, 泛型3...> {
实现类实现接口的方法时方法的类型还尚未确定;
类不仅可以使用自己的泛型,还可以使用接口的泛型;
}package com.hqyj.demos; public class Demo05_Inter { public static void main(String[] args) { MyClass03<String> mc = new MyClass03<>(); MyClass04<Integer, String> mc4 = new MyClass04<>(); mc4.test04(1001, "张三"); } } interface MyInter<T> { public abstract void test01(T t); public abstract T test02(); public abstract T test03(T t); } class MyClass05<T, String> implements MyInter<String> { public void test04(T t, String str) { } @Override public void test01(String string) { } @Override public String test02() { return null; } @Override public String test03(String string) { return null; } } //类在实现接口泛型的同时,自己也有泛型,此时类中的方法既可以使用类的泛型,可以使用接口的泛型 class MyClass04<T, E> implements MyInter<T> { public void test04(T t, E e) { } @Override public void test01(T t) { } @Override public T test02() { return null; } @Override public T test03(T t) { return null; } } /** * 类实现接口泛型的时候,泛型类型没有确定 * 注意:在类的声明中,类名之后也必须跟上泛型类型 * 类名和接口名后面的泛型类型名字必须保持一致 */ class MyClass03<T> implements MyInter<T> { @Override public void test01(T t) { } @Override public T test02() { return null; } @Override public T test03(T t) { return null; } } //1、实现类实现接口的时候,接口泛型的类型已经确定 class MyClass02 implements MyInter<String> { @Override public void test01(String s) { } @Override public String test02() { return null; } @Override public String test03(String s) { return null; } }
(六)泛型通配符
1、符号:?
2、使用泛型的时候,没有具体类型的泛型声明【T】,而是使用了和【T】有关的类型,如果要表示和【T】有关的类型,就需要使用泛型通配符【?】
3、第一种形式:使用【?】来表示任意类型
package com.hqyj.demos; import java.util.ArrayList; import java.util.Collection; public class Demo06 { //containsAll(Collection<?> c) //如果此 collection 包含指定 collection 中的所有元素,则返回 true。 public static void main(String[] args) { Collection<String> list01 = new ArrayList<>(); list01.add("qwe"); list01.add("asd"); list01.add("zxc"); list01.add("fgh"); list01.add("ert"); Collection<String> list02 = new ArrayList<>(); list02.add("qwe"); list02.add("asd"); list02.add("ggg"); System.out.println(list01.containsAll(list02));; } }
4、第二种形式:【? extends E】:确定上边界,表示参数类型是调用者类型的子类或者参数类型和调用者类型保持一致
//【? extends E】确定上边界 private static void test02() { //addAll(Collection<? extends E> c) //将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。 Collection<Student> list01 = new ArrayList<>(); Collection<Person> list02 = new ArrayList<>(); Collection<Student> list03 = new ArrayList<>(); //list01.addAll(list02); //参数类型是调用者类型的子类 list02.addAll(list01); //参数类型和调用者类型保持一致 list01.addAll(list03); }
5、第三种形式【? super E】:确定下边界;表示泛型是E的父类或者E泛型本身,不能是E类型的子类或者是无关类
public static void main(String[] args) { //确定下边界 //fill(List<? super T> list, T obj) //使用指定元素替换指定列表中的所有元素。 ArrayList<Student> li01 = new ArrayList<>(); ArrayList<String> li02 = new ArrayList<>(); ArrayList<Student> li03 = new ArrayList<>(); Collections.fill(li01, new Student()); Collections.fill(li02, "new Person()"); }
二、Set
(一)概述
1、Set是Collection的子接口
2、特点:不可重复:不能存储重复的元素
3、实现类:
(1)HashSet:底层是哈希表
(2)LinkedHashSet:底层是哈希表+链表
(二)Set遍历
1、没有特有的方法,使用Collection接口中的方法遍历Set集合
2、第一种:toArray():转数组,遍历数组
3、第二种:toArray(T[] a):转数组,遍历数组
4、第三种:迭代器遍历
5、第四种:增强for循环遍历
(1)格式
for(元素的数据类型 元素名称 : 要遍历的集合或者数组) { }
(2)说明:增强for循环的底层也是迭代器,如果使用增强for循环遍历的时候,使用集合进行操作,就会发生并发修改异常
package com.hqyj.demos; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; public class Demo08_ToArray { public static void main(String[] args) { Set<String> set = new HashSet<>(); set.add("asd"); set.add("qwe"); set.add("xcv"); set.add("ghj"); //先给定一个数组,将数组传进方法中, // 当数组的长度不足时,新建数组存储集合元素; // 当长度超出时,超出部分使用默认值进行填充 String[] arr = new String[1]; String[] array = set.toArray(arr); for (int i = 0; i < array.length; i++) { System.out.print(array[i] + " "); } } }
package com.hqyj.demos; import java.util.HashSet; import java.util.Set; public class Demo09_Foreach { public static void main(String[] args) { Set<String> set = new HashSet<>(); set.add("asd"); set.add("qwe"); set.add("xcv"); set.add("ghj"); for (String ele : set) { //不能在这里使用集合操作元素 //set.add("qqq"); System.out.print(ele + " "); } //增强for循环遍历数组 int[] arr = {12, 54, 76, 43, 98, 51, 34, 50}; for (int ele : arr) { System.out.print(ele + " "); } } }
三、HashSet去重原理
(一)存储自定义类型元素
1、某个obj对象将要存储进HashSet集合的时候,首先要计算对象的哈希码值
2、在集合中所有的哈希码值都和obj对象的码值不同的时候,进行存储
3、如果存在和obj对象相同的哈希码值,并不能说明obj对象就存储在集合中
4、需要进一步重写equals方法,判断obj对象和集合中的对象是否相等
5、如果不相等直接存储,如果相等,进行覆盖
(二)原理总结
1、重写重写hashCode方法,让不同的对象尽量拥有相同的哈希码值
2、重写equals方法,比较属性值,如果相等进行覆盖,如果不相等进行存储
四、Map
(一)概述
1、体系位置:双列集合的顶层接口
2、数据结构:描述的是一个数据(Key)到另一个数据(Value)的映射关系
(1)Key(键):有规律的,容易记忆的,不可重复的
(2)Value(值):没有规律的,不容易记忆的,可重复的
(3)操作:通过键寻找值
package com.hqyj.demos; import java.util.HashMap; import java.util.Map; public class Demo12_Map { public static void main(String[] args) { Map<Integer, String> map = new HashMap<>(); map.put(1001, "张三"); map.put(2345, "张三"); map.put(2345, "李四"); map.put(6788, "张三"); map.put(4762, "张三"); map.put(4762, "王五"); map.put(8563, "张三"); System.out.println(map); } }
(二)常用方法
1、put(K key, V value):添加功能;当集合中不存在指定键时,将键值对添加到map集合中;
修改功能;当集合中存在指定键的时候,根据键修改对应的值
2、remove(Object key):根据键删除键值对;如果键不存在,则不操作
3、clear():清空集合
4、size():返回集合中键值对的个数
5、containsKey(Object key):判断集合中否包含执行键
6、containsValue(Object value):判断集合中否包含执行值
五、Map集合
(一)Map集合第一种遍历
1、获取Map集合中所有的键,存储在一个Set集合中,之后遍历Set集合,获取每一个键,根据键获取对应的值
2、获取Map集合中所有的键:keySet()
3、遍历Set集合
(1)迭代器遍历
(2)增强for循环遍历
4、获取每一个键之后,根据键获取值的方法:get(Object key)
package com.hqyj.demos; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class Demo01_PrintMapOne { public static void main(String[] args) { Map<Integer, String> map = new HashMap<>(); map.put(234, "qwe"); map.put(345, "asd"); map.put(745, "zxc"); map.put(247, "sdf"); map.put(542, "cvb"); map.put(845, "rty"); //增强for循环简写 for (Integer key : map.keySet()) { String value = map.get(key); System.out.println(key + "..." + value); } //获取键集 Set<Integer> set = map.keySet(); //增强for循环 for (Integer key : set) { String value = map.get(key); System.out.println(key + "..." + value); } } public static void test(Map<Integer, String> map) { //获取键集 Set<Integer> set = map.keySet(); //迭代器遍历 Iterator<Integer> it = set.iterator(); while (it.hasNext()) { //Set中的元素就是Map中的每一个键 Integer key = it.next(); //根据键获取对应的值 String value = map.get(key); System.out.println(key + "..." + value); } } }
(二)Map集合第二种遍历
1、获取Map集合中所有的键值对对象(Entry),存储在一个Set集合中,遍历Set集合,获取每一个键值对对象,从键值对对象中,获取键和值
2、根据Map集合中提供的获取键值对对象的方法:entrySet()
Set<Map.Entry<Key, Value>> set = map.entrySet();
3、遍历Set集合,获取每一个键值对对象:迭代器遍历、增强for循环遍历
4、得到Entry对象之后,从对象中获取Key和Value
(1)获取Key:getKey()
(2)获取Value:getValue()
package com.hqyj.demos; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class Demo02_PrintMapTwo { public static void main(String[] args) { Map<Integer, String> map = new HashMap<>(); map.put(234, "qwe"); map.put(567, "yyy"); map.put(345, "asd"); map.put(745, "zxc"); map.put(247, "sdf"); map.put(542, "cvb"); map.put(845, "rty"); //增强for循环简写 for(Map.Entry<Integer, String> entry : map.entrySet()) { Integer key = entry.getKey(); String value = entry.getValue(); System.out.println(key + "..." + value); } //获取键值对对象 Set<Map.Entry<Integer, String>> set = map.entrySet(); //增强for循环遍历 for(Map.Entry<Integer, String> entry : set) { Integer key = entry.getKey(); String value = entry.getValue(); System.out.println(key + "..." + value); } } //迭代器遍历 public static void test(Map<Integer, String> map) { //获取键值对对象 Set<Map.Entry<Integer, String>> set = map.entrySet(); //获取迭代器对象 泛型类型和Set集合保持一致 Iterator<Map.Entry<Integer, String>> it = set.iterator(); while (it.hasNext()) { //获取每一个键值对对象 Map.Entry<Integer, String> entry = it.next(); //根据键值对中的方法获取对应的键和值 Integer key = entry.getKey(); String value = entry.getValue(); System.out.println(key + "..." + value); } } }
(四)HashMap
1、是Map集合的子实现类
2、HashMap存储JDK提供类型的元素都可以去重
3、HashMap存储自定义类型元素作为键,无法保证键的唯一性:原因:和HashSet的去重原理一样,想要去重,在自定义类型所在类中重写hashCode和equals方法
4、HashMap和HashSet:HashSet就是HashMap键的那一列,HashSet是由HashMap实现出来的
(五)HashMap和Hashtable区别
1、相同点:
(1)HashMap和Hashtable都是map接口的实现类,都是双列集合,底层都是哈希表
2、不同点:
(1)HashMap:不安全,Hashtable:安全
(2)HashMap:效率高,Hashtable:效率低
(3)HashMap:允许存储 null 值 null 键,Hashtable:不允许
(4)HashMap:初始容量为16,Hashtable:初始容量为11
(5)HashMap:扩容为原来的两倍,Hashtable:扩容为原来的两倍+1
(6)HashMap:会重新计算哈希码值,Hashtable:会直接使用元素的hashCode
(7)HashMap:出现在JDK1.2,Hashtable:出现在JDK1.0
六、File类型
(一)概述
1、File类型:用于表示一个文件或者文件夹的路径
2、路径:用于描述文件或者文件夹存储位置的字符串
(1)绝对路径:从根目录开始的路径【C:\User\Desktop\23031\核心\day4\笔记】
(2)相对路径:从项目路径开始的【src\com\demos\Person.java】
(二)构造方法
1、File(String pathname):将一个字符串表示的路径封装为File对象
2、File(String parent, String child):将父级路径和子级路径封装为一个File对象,对象中的父级路径是字符串
3、File(File parent, String child):将父级路径和子级路径封装为一个File对象,对象中的父级路径是File对象
package com.hqyj.demos; import java.io.File; public class Demo06_File { public static void main(String[] args) { File file = new File("src\\com\\hqyj\\demos"); File file1 = new File(file, "Person.java"); System.out.println(file1); File file2 = new File("src\\com\\hqyj\\demos", "Person.java"); System.out.println(file2); } }
(三)创建文件的方法
1、createNewFile():当且仅当不存在具有此抽象路径名指定名称的文件时,创建一个新的文件
2、mkdir():只能创建单个文件夹,当且仅当File对象后表示的抽象文件夹路径不存在的时候,
3、mkdirs():可以创建嵌套文件夹,如果父级路径不存在,直接创建
-
(四)练习
在D盘下的a/b/c/d目录下创建一个文件,名称叫做Helloworld.txt
-
package com.hqyj.demos; import java.io.File; import java.io.IOException; public class Demo08_Exercise { public static void main(String[] args) throws IOException { /** * 在D盘下的a/b/c/d目录下创建一个文件,名称叫做HelloWorld.txt * D:a/b/c/d * D:a\\b\\c\\d */ //创建路径 File file = new File("D:a/b/c/d"); //创建嵌套文件夹 file.mkdirs(); //新的路径是父级路径+文件名 File newFile = new File(file, "HelloWorld.txt"); //创建文件 newFile.createNewFile(); } }
(五)删除的方法
1、delete():删除指定路径的文件或者文件夹
2、注意
(1)使用该方法删除的文件夹只能是空文件夹
(2)使用该方法删除不走回收站
-
(六)重命名方法
1、renameTo(File dest):将调用者对象表示的文件使用参数对象表示的新名称重新命名
2、注意:
(1)在同路径下,该方法是重命名
(2)在不同路径下,该方法是剪切并且重命名
(七)判断的方法
1、exists():判断调用者表示的抽象路径是否存在
2、isFile():判断调用者表示的抽象路径是一个文件
3、isDirectory():判断调用者表示的抽象路径是一个文件夹
(八)获取功能
1、getAbsoluteFile():获取当前File表示的内容的绝对路径
2、getName():获取文件或者文件夹的名称
3、list():获取File对象指代的文件夹下的所有文件和文件夹存储在一个String数组中
4、length():获取文件的长度,长度指的是字节数
5、listFiles():将调用者对象表示的文件夹下,每一个文件或者文件夹都封装为一个File对象存储在一个File数组中
6、getPath():将File对象表示的路径转为字符串
(九)练习
键盘录入一个字符串,该字符串表示一个文件夹路径,如果不是文件夹则重新录入
打印当前文件夹下,所有大于5M的后缀名为.mp4格式的文件的绝对路径
package com.hqyj.demos; import java.io.File; import java.util.Scanner; public class Demo13_Exercise { /** * 键盘录入一个字符串,该字符串表示一个文件夹路径,如果不是文件夹则重新录入 * 打印当前文件夹下,所有大于5M的后缀名为.mp4格式的文件的绝对路径 */ public static void main(String[] args) { File file = getDir(); //遍历文件夹中的内容 File[] arr = file.listFiles(); //遍历数组获取每一个文件或者文件夹 for (File path : arr) { //判断是不是文件 if (path.isFile()) { //获取文件的名字 String name = path.getName(); //获取文件名最后一个点出现的索引 int index = name.lastIndexOf("."); //截取点直到结尾就是后缀名 String suffixName = name.substring(index); //判断文件的后缀是不是.mp4格式的 if (suffixName.equals(".mp4")) { //判断文件的长度是否大于5M 8bit KB MB GB TB if (path.length() / 1024 > 5) { System.out.println(path.getAbsoluteFile()); } } } } } //判断当前录入的是不是一个文件夹路径 public static File getDir() { Scanner sc = new Scanner(System.in); System.out.println("请录入一个文件夹路径:"); while (true) { String path = sc.nextLine(); //将String转为File File file = new File(path); //判断当前字符串是不是合法的文件夹路径 if (file.isDirectory()) { return file; } else { System.out.println("请录入合法的文件夹路径:"); } } } }
七、IO
(一)简介
1、IO是Input和Output的缩写,Input:输入;Output:输出
2、所有的输入输出都是基于内存的角度去考虑,一切进内存的操作叫做输入,一切出内存的操作叫做输出
3、内存:运行速度较快,是运行着的程序活跃的场地,断电丢失,不能持久化
4、磁盘:是用于存储数据的仓库,相较于内存运行较慢,断电不丢失,数据持久化存储
5、Java中操作设备之间数据传输的对象,都是IO对象,这些对象所属的类型都在IO包底下
(二)IO分类
1、按照功能分:
(1)字节流:可以直接操作字节的流对象
(2)字符流:可以直接操作字符的流对象
2、按照流向
(1)输入流:其它设备到内存
(2)输出流:内存到其他设备
3、IO流体系结构
(1)字节流:
-
字节输入流
-
字节输出流
-
字符输入流
-
字符输出流
八、字节流
(一)概述
1、可以直接操作字节信息的流对象
2、字节输入流的顶层抽象父类:InputStream
3、字节输出流的顶层抽象父类:OutputStream
4、根据交互设备的不同,具有不同的子实现类
(二)InputStream
1、常用方法:
(1)close():关闭资源
(2)read():从当前的字节流中读取一个字节信息并且返回,到达文件末尾返回-1
(3)read(byte[] b):从输入流中读取一定数量的字节信息,并将其存储在字节数组中,到达文件末尾返回-1
(4)available():返回流中剩余的字节个数
2、InputStream是一个抽象类,不能创建对象,根据子实现类的不同,具有不同的功能那个
(三)FileInputStream
1、是InputStream的子实现类,用于和磁盘上的文件进行交互
2、FileInputStream不仅可以一次读取一个字节信息,还能够一次读取多个字节信息,不仅能够操作纯文本文件,还能够操作图片、音频、视频等等格式的文件
3、构造方法:
(1)FileInputStream(File file):将一个文件的路径封装为File对象封装在FileInputStream的存储列表中,创建一个可以用来读取字节信息的流对象
(2)FileInputStream(String name):将一个文件的路径封装为字符串封装在FileInputStream的存储列表中,创建一个可以用来读取字节信息的流对象
package com.hqyj.demos;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Demo01_FileInputStream {
public static void main(String[] args) throws IOException {
//创建字节输入流对象
FileInputStream fis = new FileInputStream("aaa.txt");
System.out.println((char) fis.read());
System.out.println((char) fis.read());
System.out.println((char) fis.read());
System.out.println(fis.read());
//关闭资源
fis.close();
}
}
(四)OutputStream
1、字节输出流的顶层抽象父类
2、常用方法:
(1)close():关闭资源
(2)write(int b):将一个字节信息输出到指定设备中
(3)write(byte[] b):将一个字节数组的所有字节信息输出到指定设备中
(4)write(byte[] b, int off, int len):将一个字节数组的一部分字节信息输出到指定设备中
(五)FileOutputStream
1、构造方法
(1)FileOutputStream(File file):将file对象描述的文件路径封装为一个FileOutputStream对象
(2)FileOutputStream(String name):将字符串对象描述的文件路径封装为一个FileOutputStream对象
2、成员方法:都来自于抽象父类
(六)文件拷贝
1、含义:将一个文件中的数据,拷贝到另一个文件中
2、本质:从一个文件中,使用输入流将字节信息读取到内存中,使用输出流,将字节信息输出到另一个文件中去
package com.hqyj.demos;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo03_CopyFile {
public static void main(String[] args) throws IOException {
//创建字节输入流对象,用于将文件字节信息读取到内存
FileInputStream fis = new FileInputStream("aaa.txt");
//创建字节输出流对象,用于将内存中的字节信息读取本地磁盘
FileOutputStream fos = new FileOutputStream("b.txt");
int b;
while ((b = fis.read()) != -1) {
fos.write(b);
}
fis.close();
fos.close();
}
}
(七)文件拷贝效率提升
1、一次访问一个字节信息,效率过慢,IO的次数过多,有多少个字节信息,就需要IO2倍
2、提升的思路:利用官方提供的读写方法在参数列表中提供数组进行拷贝
3、提升效率使用方法
(1)read(byte[] b):输入
(2)write(byte[] b, int off, int len):输出
package com.hqyj.demos;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo04_CopyFilePlus {
public static void main(String[] args) throws IOException {
//创建字节输入流对象,用于将文件字节信息读取到内存
FileInputStream fis = new FileInputStream("Python.txt");
//创建字节输出流对象,用于将内存中的字节信息读取本地磁盘
FileOutputStream fos = new FileOutputStream("plus2.txt");
long start = System.currentTimeMillis();
//8192 是1024的八倍
byte[] arr = new byte[8192];
int len;
while ((len = fis.read(arr)) != -1) {
fos.write(arr, 0, len);
}
long end = System.currentTimeMillis();
fis.close();
fos.close();
System.out.println(end - start);
}
}
(八)高效缓冲字节流
1、BufferedInputStream和BufferedOutputStream
2、是包装类,本身不具有读写的功能,但是可以为具有读写功能的流对象进行功能的加强
3、构造方法:
(1)高效缓冲字节输入流:BufferedInputStream(InputStream in)
(2)高效缓冲字节输出流:BufferedOutputStream(OutputStream out)
4、原理:高效缓冲字节输入输出流都在底层设计了一个数组,用于进行拷贝,效率会大幅度进行提升
package com.hqyj.demos;
import java.io.*;
public class Demo05_BufferedInputStream {
public static void main(String[] args) throws IOException {
//创建字节输入流的实现类
/*FileInputStream fis = new FileInputStream("Python.txt");
//创建高效缓冲输入流用于提高效率
BufferedInputStream bis = new BufferedInputStream(fis);
//创建字节输出流的实现类
FileOutputStream fos = new FileOutputStream("p1.txt");
//创建高效缓冲输出流用于提高效率
BufferedOutputStream bos = new BufferedOutputStream(fos);*/
//创建高效缓冲输入流用于提高效率
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("Python.txt"));
//创建高效缓冲输出流用于提高效率
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("p1.txt"));
long start = System.currentTimeMillis();
int len;
while ((len = bis.read()) != -1) {
bos.write(len);
}
long end = System.currentTimeMillis();
//fis.close();
bis.close();
//fos.close();
bos.close();
System.out.println(end - start);
}
}
(九)输出流close和flush方法
1、close方法实际上会调用flush方法
2、flush方法:刷新此输出流并强制写出缓冲区中所有的字节信息
3、close方法用于关闭流对象,使用之后,流对象就无法进行操作;flush使用之后流依然可以使用
4、不要频繁使用flush该方法,会增加访问磁盘的次数,降低系统效率
九、字符流
(一)使用字节流出现的问题
1、GBK:国标码,只有中文和英文,中文占2个字节,英文占1个字节
2、UTF-8:万国码,世界上各个国家的语言,中文占3个字节,英文占1个字节
3、使用字节流拷贝,如果掺杂人为阅读会出现乱码情况,使用字符流解决这样的问题
(二)字符流
1、顶层抽象父类:字符输出流:Reader、字符输出流:Writer
2、常用方法:
(1)read()
(2)read(char[] cbuf)
(3)write(int c)
(4)write(String str)
(5)write(char[] cbuf, int off, int len)
(6)write(String str, int off, int len)
(7)close()
3、构造方法:
(1)FileReader(File file)
(2)FileReader(String fileName)
(3)FileWriter(File file)
(4)FileWriter(String fileName)
package com.hqyj.demos;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Demo06_Reader {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("test.txt");
FileWriter fw = new FileWriter("t2.txt");
int ch;
while ((ch = fr.read()) != -1) {
fw.write(ch);
System.out.println((char) ch);
}
fr.close();
fw.close();
}
}
(三)字符流文件拷贝
1、使用字符流拷贝文件发现,效率很高,这是因为底层封装了数组,单纯的字符流比字节流慢
2、字符流在拷贝数组的时候,底层也是基于字节流,只不过是多了一套编解码操作,这样会相对耗时,如果不掺杂人为阅读,建议使用字节流进行拷贝
package com.hqyj.demos;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Demo06_Reader {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("Python.txt");
FileWriter fw = new FileWriter("t3.txt");
long start = System.currentTimeMillis();
int ch;
while ((ch = fr.read()) != -1) {
fw.write(ch);
//System.out.println((char) ch);
}
long end = System.currentTimeMillis();
fr.close();
fw.close();
System.out.println(end - start);
}
}
(四)字符流能否进行非纯文本文件的拷贝
1、字符流不能拷贝非纯文本文件
2、原因:当字符流借助字节流拷贝字节信息到内存之后,会先将字节信息按照字符集进行解码,但是如果要拷贝的是一个图片,并不存在字符信息,一定非要按照字符集进行解码就会导致文件的损坏,最终就会导致目标文件损坏
(五)高效缓冲字符流
1、BufferedReader和BufferedWriter
2、构造方法:
(1)BufferedReader(Reader in)
(2)BufferedWriter(Writer out)
3、特有方法
(1)readLine():读取一行文本,到达文件末尾返回null
(2)newLine():换行
package com.hqyj.demos;
import java.io.*;
public class Demo09_ReadLine {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("Python.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("p5.txt"));
String str;
while ((str = br.readLine()) != null) {
bw.write(str);
bw.newLine();
}
br.close();
bw.close();
}
}
十、转换流
(一)概述
1、InputStreamReader:是字节流通向字符流的桥梁
(1)构造方法:InputStreamReader(InputStream in, String charsetName)
(2)创建一个转换流对象,可以把将来方法中接收的字符,通过指定的charsetName字符集解码成字节信息
2、OutputStreamWriter:字符流通向字节流的桥梁
(1)构造方法:OutputStreamWriter(OutputStream out, String charsetName)
(2)创建一个转换流对象,可以把将来内存中的字符,按照指定的字符集编码,将编码后的字节信息输出到指定文件
十一、Properties
(一)概述
1、Properties:对象表示一个持久的属性集
(1)属性集:还是一个双列集合,表示的是属性名和属性值的对应关系
(2)持久:可以将磁盘上存储的文件用流读取进Properties类中,也可以将Properties中的属性集通过流输出到文件中
2、Properties没有泛型,是因为Properties的键和值全都是String类型的数据,所以不需要书写泛型
3、Properties是Hashtable的子类,也可以充当双列集合,但是没有必要
(二)特有方法
1、getProperty(String key):根据字符串类型的键,获取其对应的字符串的值
2、setProperty(String key, String value):调用Hashtable的put方法添加键值对
3、stringPropertyNames():返回键集,类似于keySet方法
(三)配置文件交互
1、load(InputStream inStream):将配置文件中的键值对信息读取到Properties对象中
2、store(OutputStream out, String comments):将Properties中的属性集和指定的注释输出到配置文件中
#Update name zhangsan to dilireba, age 23 to 18
#Fri Apr 07 10:33:51 CST 2023
age=18
name=dilireba
className=com.hqyj.demos.Person
package com.hqyj.demos;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class Demo03_Load {
public static void main(String[] args) throws IOException {
Properties pro = new Properties();
//使用load方法读取配置文件中的键值
pro.load(new FileInputStream("config.properties"));
//获取对象或者配置文件中的值
System.out.println(pro.getProperty("name"));
System.out.println(pro.getProperty("age"));
System.out.println(pro.getProperty("className"));
//修改配置文件
pro.setProperty("name", "dilireba");
pro.setProperty("age", "18");
//将对象中的属性集输出到指定文件
pro.store(new FileOutputStream("config.properties"), "Update name zhangsan to dilireba, age 23 to 18");
}
}
十二、网络编程
(一)概述
1、网络:计算机网络,又在不同地理位置,不同的计算机互联形成的一个计算机系统
2、网络编程:在已经拥有的完备成熟的网络系统中,在整个系统之上,使用网络进行编程,是针对应用层的设计活动
(二)网络编程三要素
1、IP地址
1、IP地址就是计算机在网络中的唯一标识
2、分类
(1)IPV4:
-
构成,点分十进制构成的,由4个0-255之间的数组组成,来表示一个地址,4个字节构成即32位,能够表示40多亿的地址
-
四个字节中,有2个字节或者3个字节表示所在子网
-
一个字节能够表示0-255个数字,但是不能全用
-
0表示子网网号。198.168.6.0
-
255表示广播地址:广播地址发送的内容在子网中所有主机都可以接收到
-
特殊IP:127.0.0.1;本地回环地址,可以访问本机
-
相关命令:ipconfig查看当前网卡信息
-
ping:可以查看网络是否畅通
(2)IPV6:
-
构成:由8组数字组成,每组数字都是4个16进制的数字,每个数字有16种状态,32个数字,最多能表示16^32个ip地址
-
号称可以给地球上的每一粒沙子赋予一个ip地址
2、端口号
1、也是一个数字,用于标记一台电脑中的某个进程
2、端口号的取值范围:0-65535
3、意味着计算机中至多可以运行65535个进程,当程序运行的时候,计算机会分配一个独一无二的端口号给进程,我们通过端口号,就可以找到指定的进程,当程序结束时,端口号就会被回收
4、分配:可以计算机随机分配,也可以指定端口号给进程
5、常用的端口号
(1)操作系统:0-1024之间
(2)MySQL:3306
(3)Tomcat:8080
(4)QQ:4000
(5)Orale:1521
3、通信协议
1、用于定义通信双方在交互时,对戏惊喜得到封装好和解析的规则,就是协议
2、网络分层:
(1)应用层:http、https(规定数据如果封装以及解析)
(2)传输层:UDP、TCP(更多关注的是端对端的传输)
(3)网络层:IP地址(如果完成两台设备之间的传输)
(4)物理层:底层的硬件设备、数据完整性的校验
(三)InetAddress
1、InetAddress的对象表示IP地址
2、对象获取的方法
(1)getByName(String host):根据主机名返回对象包含IP地址
(2)getByAddress(byte[] addr):给定装有ip地址的数组,返回InetAddress对象
(3)getAllByName(String host):根据主机名获取所有当前类型对象的数组
(4)getLocalHost():获取本机的主机名和IP地址
3、常用方法
(1)getHostName():获取主机名称
(2)getAddress():获取ip地址所在的数组
(3)toString():获取主机名和IP地址的字符串表示形式
package com.hqyj.demos;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class Demo04_InetAddress {
public static void main(String[] args) throws UnknownHostException {
//DESKTOP-8MJ4BMR
InetAddress host = InetAddress.getLocalHost();
String name = host.getHostName();
System.out.println(name);
//-64,-88,88,1
byte[] arr = host.getAddress();
for (byte ele : arr) {
System.out.println(ele);
}
//DESKTOP-8MJ4BMR/192.168.88.1
String str = host.toString();
System.out.println(str);
}
public static void test() throws UnknownHostException {
//DESKTOP-8MJ4BMR/192.168.88.1
InetAddress name = InetAddress.getByName("DESKTOP-8MJ4BMR");
System.out.println(name);
///192.168.6.46
byte[] arr = {(byte) 192, (byte) 168, 6, 46};
InetAddress address = InetAddress.getByAddress(arr);
System.out.println(address);
//所有ipv4+ipv6的地址
InetAddress[] all = InetAddress.getAllByName("DESKTOP-8MJ4BMR");
for (InetAddress ele : all) {
System.out.println(ele);
}
//DESKTOP-8MJ4BMR/192.168.88.1
InetAddress host = InetAddress.getLocalHost();
System.out.println(host);
}
}
(四)UDP协议和TCP协议
1、概述
1、UDP和TCP都是传输层协议,都是端到端的协议
2、区别:
(1)UDP协议:面向无连接。类似:发短信、寄快递,效率高,不安全,先发送的消息未必先收到,只区分发送端和接收端
(2)TCP协议:面向连接。类似打电话,先发送的消息一定先到达,安全,效率低,区分客户端和服务器端
2、Socket编程
1、Socket:是两台计算机之间通信的端点,类似生活中的码头、驿站
2、Socket:也叫作套接字
(1)UDP中使用的套接字是:DatagramSocket
(2)TCP中:客户端使用的套接字是Socket,服务器端使用的套接字是ServerSocket和Socket
3、TCP编程
1、客户端使用的套接字是Socket,服务器端使用的套接字是ServerSocket和Socket
2、客户端和服务器端Socket的区别;
(1)客户端:使用Socket的构造方法直接创建Socket对象
(2)服务器端:不能直接使用Socket创建对象;而是通过ServerSocket对象,从端口捕获客户端发送的请求,专门为此建立一个Socket对象,并做出响应处理
3、构造方法
(1)Socket(String host, int port):创建一个通信点对象,专门用于和指定IP的主机中指定端口号进行通信,这个对象一旦创建成功就说明连接成功了
(2)创建对象的过程就是在和服务器端进行连接
(3)ServerSocket(int port):创建一个服务器通信点对象
(4)accept():专门用于接收客户端请求,并且返回一个对应的Socket对象
4、传出操作
(1)getInputStream():返回次套接字的输入流
(2)getOutputStream():返回次套接字的输出流
十三、多线程
(一)程序和进程
1、程序:一个固定的存储有逻辑和数据的集合,是一个静态的概念,存储在磁盘上
2、进程:一个正在执行着的程序,是一个动态的概念,一个运行在计算机内存中
(二)进程和线程
1、进程:是一个正在运行的程序,会分配一部分系统资源,是一个独立的资源分配单位
2、线程:一条正在独立执行的路径,多线程。在执行某个程序的时候,该程序与多个子任务,每一个线程都可以独立的完成其中一个子任务,在子任务之间,没有什么依赖关系。可以独立执行
3、进程和线程关系
(1)进程是用于分配系统资源的单位
(2)一个进程中,可以有多个线程,但是一个进程中,至少有一条线程
(3)线程不会独立分配资源,一个进程中的所有线程,共享的是同一个进程的资源
(三)并行和并发
1、并行:在一个时间点,有多个任务(进程、线程)正在执行,多核心,多CPU编程
2、并发:在一个时间点,有多个任务同时发起,但是在某一个时间点,只能有一个进程或者线程在执行任务,单核心,单CPU编程
(四)问题
CPU在多个任务之间来回切换,效率是提高了还是降低了
1、对于一个任务而言,效率是降低了
2、对于整个计算机而言,效率是提高了