泛型
250 泛型概述
看不懂图片可以看:https://zhuanlan.zhihu.com/p/78811004
使用泛型最直观的例子就是:Collection<String> c1=new ArrayList<String>();
,其中的String就是数据的操作类型,把数据的操作类型指定为参数就称为:泛型。
251 泛型类
泛型类就是把泛型定义在类上,用户使用该类的时候,才把类型明确下来
/*
1:把泛型定义在类上
2:类型变量定义在类上,方法中也可以使用
*/
public class ObjectTool<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
测试代码:
public static void main(String[] args) {
//创建对象并指定元素类型,泛型的使用!!!
ObjectTool<String> tool = new ObjectTool<>();
tool.setObj(new String("钟福成"));
String s = tool.getObj();
System.out.println(s);
//创建对象并指定元素类型,泛型的使用!!!
ObjectTool<Integer> objectTool = new ObjectTool<>();
/**
* 如果我在这个对象里传入的是String类型的,它在编译时期就通过不了了.
*/
objectTool.setObj(10);
int i = objectTool.getObj();
System.out.println(i);
}
252 泛型方法
1.泛型类在使用的时候需要制定参数,而泛型方法会自动识别传入的参数类型,所以在使用方法上:使用泛型类需要写类型参数,而使用泛型方法的时候不需要写类型参数(与正常调用方法无异)。
2.在定义上泛型方法也与类有很大区别,泛型符号是写在返回值类型前面滴。
253 泛型接口
泛型接口:
/*
把泛型定义在接口上
*/
public interface Inter<T> {
public abstract void show(T t);
}
泛型接口的实现类:
当子类不明确泛型类的类型参数变量时,外界使用子类的时候,也需要传递类型参数变量进来,在实现类上需要定义出类型参数变量。
/**
* 子类不明确泛型类的类型参数变量:
* 实现类也要定义出<T>类型的
*
*/
public class InterImpl<T> implements Inter<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
测试类代码与泛型类基本一致。
254 泛型——类型通配符
类型通配符是在用泛型创建对象的时候使用的,目的是限制类别。
建议看链接中的3.4类型通配符,写的很好。
255 泛型——可变参数
需求分析:写出一个方法,要求求任意多个int类型数据之和,传入的参数的个数是不确定的,可变参数其实说的是参数的个数可变。
执行下列代码:
public static void main(String[] args) {
sum(10,15);
}
public static void sum(int... a){
System.out.println(a);
}
输出结果为:
[I@3ac3fd8b
说明a实际是一个数组,一个储存了传入所有数据(int类型)的数组,因此实现求和方法将代码改为:
public static void main(String[] args) {
sum(10,15);
}
public static void sum(int... a){
int temp =0;
for (int i:a){
temp +=i;
}
System.out.println(temp);
}
可变参数要放在最后是因为在上一个代码块中int....a
会吸收所有的int形式变量,如果需要单独传入某个特殊参数不方便操作,输入int....a,int b
会报错,必要时参数要写作int b,int...a
。
255 泛型——可变参数的使用
1.接口里面可以有静态方法,原来讲课的时候没有讲到,这属于新特性。(静态方法可以直接通过类名调用。)
2.注意三种形式生成的表类型有所区别,操作限权每个都不同。(都有查看的方法,都不能改变表的元素个数。)
3.对set集合没有修改方法的说明:set集合特点就是不支持带索引的操作,而set方法是带索引的方法,肯定是不支持的。
257 Map集合
HashMap users = new HashMap();
users.put("11", "张浩太"); // 将学生信息键值对存储到Map中
users.put("22", "刘思诚");
users.put("33", "王强文");
users.put("44", "李国量");
users.put("55", "王路路");
需要注意的是:key不能重复,如果写入重复的key,那么新的值就会替代原来的值。换句话说put
方法在key不同时就是录入数据,在key相同时,如输入再输入users.put("22", "思无邪");
,那么22对应的就是思无邪了,刘思诚已经被顶替掉了。
Map-map集合基本功能
put会返回被顶替的值,remove会返回被删除的值
获取所有值集合返回的是Collection,不是Set是因为值是可以重复的,而Set集合不允许重复。
260 Map-map集合的遍历
方式1:找出键的集合,遍历所有键并且在遍历所有键的过程中找出键对应的值。范例:
public static void main(String[] args) {
Map<String,String> m1= new HashMap<>();
m1.put("num001","zhangwuji");
m1.put("num002","siwuxie");
m1.put("num003","fengqingyang");
Set<String> keySet = m1.keySet();
for (String key:keySet){//Set集合不能使用索引方法,因此不能使用普通的遍历。
String name = m1.get(key);
System.out.println(key+","+name);
}
}
方式2:使用259最后标红的方法entrySet()
方法
得到所有的键值对对象Map.Entry
遍历得到的键值对对象反找出所有得到键getKey()
和值getValue()
。
范例代码如下:
public static void main(String[] args) {
Map<String,String> m1= new HashMap<>();
m1.put("num001","zhangwuji");
m1.put("num002","siwuxie");
m1.put("num003","fengqingyang");
/* Set<String> keySet = m1.keySet(); //方式1
for (String key:keySet){
String name = m1.get(key);
System.out.println(key+","+name);
}*/
Set<Map.Entry<String, String>> entrySet = m1.entrySet();//方式2
for (Map.Entry<String, String> nic:entrySet){
System.out.println(nic.getKey()+","+nic.getValue());
}
}
267 Collections(区别:Collection是接口,Collections是类)
Collections集合操作的工具类
IO流
File
272 File类概述和构造方法
三种构造方法都能达到一样的效果,三种构造方法的测试代码:
File f1 = new File("D:\\test\\java.txt");
File f2 = new File("D:\\test","java.txt");
File test = new File("D:\\test");
File f3 =new File(test,"java.txt");
System.out.println(f1);
System.out.println(f2);
System.out.println(f3);
/*最终系统输出:
D:\test\java.txt
D:\test\java.txt
D:\test\java.txt*/
}
273 File类创建功能
对方法1:如果该文件不存在则创建该文件并返回true。如果该文件存在则不创建文件,并返回false。
方法2,3:创建的是目录,不过方法2只能创建一级目录,方法3可以创建多级目录。
注意:是创建文件还是目录是根据调用的方法,而不是根据创建File对象时输入的String的内容,如果在不存在的目录下直接创建文件,运行会报错。
275 File类判断和获取功能
276 File删除功能
对图片中相对路径:如果这么写前面默认带的是此项目的根目录,即如果写:
public static void main(String[] args) {
File file = new File("java.txt");
System.out.println(file.getAbsolutePath());
}//此时系统输出:E:\develop\java_practice\IDEA_code\java.txt
276 递归
比如以前学过的不死神兔(有一对兔子,从出生起后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子, 假如兔子都不死,问第二十个月的兔子对数为多少?
* 规律: 1,1,2,3,5,8,13…
* 规则:A.从第三个月开始,每一项是前两项之和。
B.说明前两项是已知的。)
不死神兔的案例就可以使用递归的思想解决:
注意别忘了定义递归出口:在本问题中是前两个月的兔子数量。
public static void main(String[] args) {
System.out.println(f(20));
}
public static int f(int month){
if(month ==1|| month ==2){
return 1;
}else {
return f(month-1)+f(month-2);
}
}
278 递归遍历某目录下的所有内容
范例代码:
public static void main(String[] args) {
File f1 = new File("E:\\develop\\java_practice\\IDEA_code");
System.out.println(f1.getAbsolutePath());
finAll(f1);
}
public static void finAll(File f1){//方法的返回值为void
if (!f1.exists()){
System.out.println("path error!!!");
}
File[] listFiles = f1.listFiles();
if (listFiles == null) throw new AssertionError();
for (File file:listFiles){
if (file.isFile()){
System.out.println(file.getAbsolutePath());
}else {
finAll(file);
}
}
}
个人感悟:对本节278和上一节276,同样是定义递归函数,为何一个有返回值一个没有返回值呢:如果递归之后需要用到数据就返回,不需要就不返回呗。比如说:算5的阶乘,需要4的阶乘,所以必须返回4的阶乘,而本节扫描显示目录下的所有文件,当遇到目录时直接进入递归进入目录扫描显示即可,不需要返回扫描的结果啥的。
字节流
279 IO流概述和分类
因为字节流是万能的流,所以不知道用哪种流可以用字节流。
280 字节流写数据
注意千万不要忘记释放资源。
范例程序:
public static void main(String[] args) throws IOException {
FileOutputStream f1 = new FileOutputStream("Io\\java.txt");//第一步
f1.write(57);//第二步
f1.close(); //第三步
}//注意写入的是字节流,在文件中看到的不是字符57,而是字符9;
281 字节流写数据三种方式(write方法的三种用法)
对第三种方法的说明,意思就是从off索引开始写入len长度的数据。范例程序:
public static void main(String[] args) throws IOException {
FileOutputStream f1 = new FileOutputStream("Io\\java.txt");
f1.write(57);//方式1
byte[] b = {57,58,59,60,61};//方式2
f1.write(b);
f1.write(b,1,1);//方式3
f1.close();
}
知道写入的是字节流,显示的是字符,不能同等看待,那么想显示我想显示的字符除了百度查字节码外Java中String类自带获取字符串的byte流的方法getBytes
。范例程序(写入:“我爱Java!!!”):
public static void main(String[] args) throws IOException {//个人:注意运行此程序时会将这个java.txt文件里面的内容清空再写入
FileOutputStream f1 = new FileOutputStream("Io\\java.txt");
f1.write("我爱Java!!!".getBytes());
f1.clos();
}
第一个构造方法在内部会调用第二个构造方法,本质上做的事情都是一样的。只是单从创建字节流来说第二个方式更简单。
282 字节流写数据如何换行和追加写入
换行:想写入换行同样要把\r\n
换成字节。
追加写入:如281第二个代码块备注所写,每次运行都会清空,那就在定义字节流的时候申明要追加写入:
public static void main(String[] args) throws IOException {
FileOutputStream f1 = new FileOutputStream("Io\\java.txt",true);//注意第二个参数是false代表的也是清空原文件而不是在文件开头追加写入。
f1.write("我爱Java111!!!\n\r".getBytes());
f1.close();
}
字节流写数据加异常处理
前面在数据流写入时报错采用的是抛出的方式,现在采用try...catch
方式处理异常:
public static void main(String[] args) {
try {
FileOutputStream f1 = new FileOutputStream("Io\\java.txt",true);
f1.write("我爱Java111!!!\n\r".getBytes());
f1.close();
}catch (Exception e){
e.printStackTrace();
}
}
看似完美,但是有一个隐藏的问题,在IO处理的时候一定要保证流关闭,上述代码玩意在创建或写入字节流的时候错误了,f1将无法关闭,于是引入finally:
将代码改进为:
又出现错误,因为f1是在try里面被定义的,有可能错误,在外面想被看到于是改为在外面定义null,在内部再赋值,于是:
但是又出现了新问题,就是由于f1可能一直为null,所以close可能出现空指针异常,于是在clos那加入try....catch
,:
public static void main(String[] args) {
FileOutputStream f1 = null;
try {
f1 = new FileOutputStream("Io\\java.txt", true);
f1.write("我爱Java111!!!\n\r".getBytes());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
f1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
284 字节流读数据(一次读一个)
一个一个读字节调用的是类里面的read()
方法。
为了下面演示,现补充一个知识点,在c语言中赋值a=666
整个式子的值为1.
在Java中,赋值的是多少整个赋值式子值就是多少如:
int a;
System.out.println(a = 666);
系统输出为666。
再学习一个知识点,当FileIpuStream类里面的read方法已经读到数据末尾,会返回Byte数据值为-1。
综上,读数据的范例程序:也是字节流读数据的标准代码
public static void main(String[] args) throws IOException {
FileInputStream f2 =new FileInputStream("Io\\java.txt");
int temp;
while ((temp = f2.read())!= -1){
System.out.print((char)temp);
}
f2.close();
}//经过验证,汉字读取有问题!!!注意这点,后面字符流与这有关
字节流一次读一个字节数组数据
新知识点:1.一次读入byte数组长度的字符串的read
方法,此方法返回值为实际读取到的字符的个数,如果一个都没有读取到,那么和读一个字节一样返回-1;
2.String
的新构造方法,将byte
数组转换为字符串
3.
范例代码:
public static void main(String[] args) throws IOException {
FileInputStream fRead = new FileInputStream("Io\\java.txt");
byte[] byteRead = new byte[1024];
System.out.println(fRead.read(byteRead));
System.out.print(new String(byteRead));//将byte数组转化为字符串
}
由于测试的文件为425个字符,所以先显示425,然后显示文件内容,最后显示了很多
十分丑陋,为了避免这样的情况的发生,利用上读取到的字符的长度:
public static void main(String[] args) throws IOException {
FileInputStream fRead = new FileInputStream("Io\\java.txt");
byte[] byteRead = new byte[1024];//一般给1024及其整数倍
int length = fRead.read(byteRead);
System.out.print(new String(byteRead, 0, length));//改进后的代码,只有读取到数据的那块的byte数组转化为了字符串
}
288 字节缓冲流读/写数据
在写数据时,一个字节一个字节的写,写的次数明显很多,效率就会变得很低。
缓冲输出流的特点是:在流里维护了一个缓冲区,写字节时,先将字节写入缓冲区(缓冲区大小为8k),当缓冲区写满时,在一次性的将数据写到文件里。这样就降低了写的次数,提高了效率。读数据也是同理。
在使用方法,字节缓冲流读写数据和前面讲的FileInputStream只有在创建的时候有区别,其他使用都是一样的,字节缓冲流创建范例代码:
BufferedInputStream fread = new BufferedInputStream(new FileInputStream("Io\\1.jpg"));//传入输入流
BufferedOutputStream fwrite = new BufferedOutputStream(new FileOutputStream("Io\\2.jpg"));
字符流
在GBK编码中一个字节占两个Byte,UTF-8编码中一个字节占三个Byte,在如284的每次读取一个字节就显示时中文显示不出来,为了方便中文显示操作,出现了字符流
编码表
字符串中的编码解码问题
字符流的编码/解码问题
对中文的读取而言,如果采用以前的方法:
FileInputStream f1 =new FileInputStream("Io\\java.txt");
System.out.println((char) f1.read());
程序不会识别到中文需要多个byte因此无法正常显示中文,但是如果采用字符流定义:
InputStreamReader reader = new InputStreamReader(new FileInputStream("Io\\java.txt"), "UTF-8");
System.out.println((char) reader.read());
那么按上述代码块执行,就算是中文也能正常显示。
字符流写数据的五种方式
因为是字符流,所以将字节数组改为了字符数组。
首先需要注意字符流也必须要关闭,其次还要注意字符流本身是编码表+字节流,所以本身还是依靠字节流实现,但是字符流有缓冲区,因此在写入的时候必须使用flush()方法冲洗一下,这样才会刷新缓冲区实现写入,当然执行close()方法的时候也会默认先刷新一下,再写入:
295 字符流读数据的两种方式(一次一个/一次好多个)
注意到方法里,字节流读写数据都是字节/字节数组,字符流读写数据都是字符/字符数组。
关于使用字节流复制图片正常,而字节流复制图片错误的说明:
这是编码、解码的问题:
字符流按字符读数据:一次读两个字节,返回了这两个字节所对应的字符的int型数值(编码)。
写入文件时把这两个字节的内容解码成这个字符在Unicode码下对应的二进制数据写入。
即把原始文件中的二进制数据以字符形式读出,再将字符以二进制形式写入,所以得到的文件以字符方式存储。而图片的数据是按字节存储的,所以打开图片时解码出错了!
字节流按字节读数据:而字节不需要编码、解码,只有字节与字符之间转换时才需要编码、解码!所以可以正常读取图片数据。
所以,非纯文本不要用字符流。
————————————————
版权声明:本文为优快云博主「山淼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/qq_43229056/article/details/106137920
297 字符流读写数据简化版
改进的背景:由于使用InputStreamReader
和OutPutSreamWriter
时常常使用的也是默认编码,所以在使用默认编码时候提供了一种简单的写法(新写法是InputStreamReader
和OutPutSreamWriter
的子类),需要注意的是如果使用的不是默认编码,或者设计到编码问题,还得换回InputStreamReader
和OutPutSreamWriter
,不能使用新学的简单写法。
记忆:有reader和writer关键词的就是字符流。
299 字符缓冲流
BufferedReader和BufferedWriter,BufferedReader和BufferedWriter与FileReader
和FileWriter
的关系就好比:BufferedInputStream
和BufferedOutputStream
与FileInputStream
和FileOutputStream
的关系,名字都是少了个File,然后创建方式也可以类比。字符缓冲流创建方式范例代码如下:
BufferedReader reader = new BufferedReader(new FileReader("Io\\java.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("Io\\java1.txt"));
不包含任何终止符号也就是readline()
方法只读一行的内容读不到换行符,也就输出不了换行符。
其实想一想这两个特有功能是配套的,因为读是读内容读不到换行符,如果读完之后要写入文件还得自己手动输入换行符,因此提供一个输入换行符的功能来配套。
302 Io流小节
字节流:
读写方法
具体实现类:
字符流:
读写方式;
具体实现类:FileReader
和FileWriter
只是图中上面的简便写法,使用方法都是一样的,只是定义格式简单,还有就是只能使用默认编码。
此外还可参加:
https://www.cnblogs.com/hopeyes/p/9736642.html
311 复制文件的异常处理
注意在JDK9改进方案中,虽然会自动释放资源,但是最终还是要抛出异常(输入,输出流对象中间那个是分号,不是逗号)。
总结:第二种方案简便。