概述
小fleeting开始了第二阶段的学习啦~~
后面的啃起来有点爬坡的赶脚了呢!
2.1
异常
- 基本概念
– - Throwable是类,
Exception和Error都继承了该类,
其他具体类型继承了Exception和Error - 具体类型如下:
FileNotFoundException文件不存在异常
ParseException 解析异常,日期字符串转换为日期对象的时候,有可能抛出的异常
OutOfIndexException 数组下标越界异常
OutOfMemoryError 内存不足
ClassCastException 类型转换异常
ArithmeticException 除数为零
NullPointerException 空指针异常
-
处理
//常见手段:try catch finally
//1.将可能抛出FileNotFoundException 文件不存在异常的代码放在try里
//2.如果文件存在,就会顺序往下执行,并且不执行catch块中的代码
//3. 如果文件不存在,try 里的代码会立即终止,程序流程会运行到对应的catch块中
try{
。。。。
}
catch(。。。。Exception e){//“。。。。Exception”也可以改为其父类,即Exception和Throwable
。。。。
e.printStackTrace(); //会打印出方法的调用痕迹
}
//无论是否出现异常,finally中的代码都会被执行
finally{
。。。。
}
//多异常捕捉办法
解决办法之一是分别进行catch
另一个种办法是把多个异常,放在一个catch里统一捕捉
(不过需要通过instanceof 进行判断具体的异常类型)
package exception;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestException {
public static void main(String[] args) {
File f = new File("d:/LOL.exe");
try {
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d = sdf.parse("2016-06-03");
} catch (FileNotFoundException | ParseException e) {
if (e instanceof FileNotFoundException)
System.out.println("d:/LOL.exe不存在");
if (e instanceof ParseException)
System.out.println("日期格式解析错误");
e.printStackTrace();
}
}
}
- throws
考虑如下情况:
主方法调用method1
method1调用method2
method2中打开文件
package exception;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class TestException {
public static void main(String[] args) {
method1();
}
//那么method1就会接到该异常,通过try-catch来处理
private static void method1() {
try {
method2(); //try内是可能发生异常的method2
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
//method2需要进行异常处理,但它把这个异常通过throws抛出去
private static void method2() throws FileNotFoundException {
File f = new File("d:/LOL.exe");
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
}
}
throws与throw这两个关键字接近,不过意义不一样,有如下区别:
- throws 出现在方法声明上,而throw通常都出现在方法体内。
- throws 表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某个异常对象。
-
自定义异常
创建自定义异常
抛出自定义异常
练习:
package character;
public class Hero{
public String name;
protected float hp;
public void attackHero(Hero h)throws EnemyHeroIsDeadException{ //attackHero中进行具体的正常操作,并throw出异常
if(h.hp==0)
throw new EnemyHeroIsDeadException(h.name+"挂了,无法攻击"); //实例化自定义异常EnemyHeroIsDeadException,并将其抛出
}
class EnemyHeroIsDeadException extends Exception{ //创建一个自定义异常类EnemyHeroIsDeadException(继承Exception)
public EnemyHeroIsDeadException(){ //提供一无参的构造方法
}
public EnemyHeroIsDeadException(String msg){ //提供一 带参的构造方法,并调用父类的对应的构造方法(msg:指代现场信息)
super(msg);
}
}
public void method1(Hero h){ //method1进行异常信息处理
try{ //try内是可能发生异常的attackHero
this.attackHero(h);
}catch(EnemyHeroIsDeadException e){
System.out.println("异常的具体原因:"); //异常处理
System.out.println(e.getMessage()); //e.Message(),获得现场信息
e.printStackTrace();
}
}
public static void main(String[] args){
Hero garen=new Hero();
garen.name="盖伦";
garen.hp=616;
Hero bingzhang=new Hero();
bingzhang.name="兵长";
bingzhang.hp=0;
garen.method1(bingzhang); //main函数调用method1
}
}
练习:
关于自定义异常:类与接口;多个自定义异常;难;
对MyStringBuffer的插入和删除方法中的边界条件判断,用抛出异常来解决
例: insert(int pos, String b) , 当pos 是负数的时候,抛出自定义异常
需要实现自定义两种异常
IndexIsNagetiveException 下标为负异常
IndexIsOutofRangeException 下标超出范围异常
练习:示例3
练习-异常综合
这是一个类图
Account类: 银行账号
属性: balance 余额
方法: getBalance() 获取余额
方法: deposit() 存钱
方法: withdraw() 取钱
OverdraftException: 透支异常,继承Exception
属性: deficit 透支额
代码如下
Accoount.java:
package exception;
import exception.OverdraftException;
public class Account{
protected double balance;
public Account(double init){
this.balance=init;
}
public double getBalance(){
return balance;
}
public void deposit(double amt){
this.balance+=amt;
}
public void withdraw(double amt)throws OverdraftException{
if(this.balance-amt<0)
throw new OverdraftException("透支了",amt-this.balance);
this.balance-=amt;
}
public static void main(String[] args) {
double init=2000;
double amt=3000;
Account a=new Account(init);
//存钱500
a.deposit(500);
//查看余额
System.out.println(a.getBalance());
try{
a.withdraw(amt);
}catch(OverdraftException e){
System.out.println("透支啦"+e.getDeficit());
e.getStackTrace();
}
}
}
OverdraftException.java:
package exception;
public class OverdraftException extends Exception{
private double deficit;
public OverdraftException(String msg,double deficit){
super(msg);
this.deficit=deficit;
}
public double getDeficit(){
return deficit;
}
}
效果:
============================================================================================================================================
2.2
一、 I/O
-
文件对象
- 创建文件对象
package file;
import java.io.File;
public class TestFile {
public static void main(String[] args) {
// 使用绝对路径或者相对路径创建File对象
// 相对路径,相对于工作目录,如果在eclipse中,就是项目目录。以下为绝对路径的例子:
File f1=new File("d:/LOLFolder");
System.out.println("f1的绝对路径:"+f1.getAbsolutePath());
File f2 = new File("LOL.exe");
System.out.println("f2的绝对路径:" + f2.getAbsolutePath());
// 把f1作为父目录创建文件对象
File f3 = new File(f1, "LOL.exe");
System.out.println("f3的绝对路径:" + f3.getAbsolutePath());
System.out.println("判断是否存在:"+f3.exists());
//可以得出即使实际文件不存在也创建出路径的
}
}
//另外,通过 System.out.println(f1) 也不会出现编译错误,
//只不过f1.getAbsolutePath()得到的是String类型。
//eg:System.out.printf("最小的文件是%s",f.getAbsoluteFile());
- 文件常用办法
package file;
import java.io.File;
import java.util.Date;
public class TestFile {
public static void main(String[] args) {
File f=new File("c:/project/j2se/src/character/Hero.java");
System.out.println("当前文件是:t"+f);
System.out.println("判断文件是否存在:t"+f.exists());
System.out.println("判断是否是文件:t"+f.isFile());
System.out.println("判断是否是文件夹:t"+f.isDirectory());
System.out.println("获取文件长度:t"+f.length());
//注意!f.length()返回的是long类型。因为long类型很多运算无法进行,所以有的时候需要把它强制转换成int类
long time=f.lastModified();
System.out.println("获取文件的最后修改时间数值:t"+time);
Date d= new Date(time);
System.out.println("获取文件的最后修改时间日期:t"+d);
/*
//设置文件修改时间为1970.1.1 08:00:00
f.setLastModified(0);
//文件重命名(把Hero.java改成Hero.java)
File f2=new File("c:/project/j2se/src/character/Hero2.java");
f.renameTo(f2);
*/
}
}
package file;
import java.io.File;
import java.io.IOException;
public class TestFile {
public static void main(String[] args) {
File f=new File("c:/project/j2se/src/character");
//f.list()以字符串数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹):
System.out.println("当前文件夹下的所有文件");
String ff[]=f.list();
//输出看看
for(String i:ff)
System.out.printf("%s ",i);
System.out.println();
// f.listFiles()以文件数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
File[]fs= f.listFiles();
//f.getParent()以字符串形式返回获取所在文件夹
String f1=f.getParent();
System.out.println("该文件所在文件夹"+f1);
//f.getParentFile()以文件形式返回获取所在文件夹
File f2=f.getParentFile();
System.out.println("该文件所在文件夹"+f2);
// 创建文件夹
//如果父文件夹不存在,则用mkdirs创建一个父文件夹;若存在则mkdir。用反会报错。
//此处父文件夹src存在,故用mkdir:
f.mkdir();
/* f.createNewFile();
* 创建一个空文件,如果父文件夹skin不存在,就会抛出异常
所以创建一个空文件之前,通常都会创建父目录
f.getParentFile().mkdirs();*/
// 列出所有的盘符c: d: e: 等等
f.listRoots();
// 刪除文件
f.delete();
// JVM结束的时候,刪除文件,常用于临时文件的删除
f.deleteOnExit();
}
}
- **练习-遍历文件夹 **
一般说来操作系统都会安装在C盘,所以会有一个 C:WINDOWS目录。
遍历这个目录下所有的文件(不用遍历子目录)
找出这些文件里,最大的和最小(非0)的那个文件,打印出他们的文件名
注: 最小的文件不能是0长度
package file;
import java.io.File;
public class TestFile {
public static void main(String[] args) {
File f = new File("c:\windows");
File[] fs = f.listFiles();
if(null==fs)
return;
long minSize = Integer.MAX_VALUE;
long maxSize = 0;
File minFile = null;
File maxFile = null;
for (File file : fs) {
if(file.isDirectory())
continue;
if(file.length()>maxSize){
maxSize = file.length();
maxFile = file;
}
if(file.length()!=0 && file.length()<minSize){
minSize = file.length();
minFile = file;
}
}
System.out.printf("最大的文件是%s,其大小是%,d字节%n",maxFile.getAbsoluteFile(),maxFile.length());
System.out.printf("最小的文件是%s,其大小是%,d字节%n",minFile.getAbsoluteFile(),minFile.length());
}
}
改:遍历子文件夹
//使用递归来遍历一个文件夹的子文件
public static void listFiles(File file){
if(file.isFile()){
if(file.length()>maxSize){
maxSize = file.length();
maxFile = file;
}
if(file.length()!=0 && file.length()<minSize){
minSize = file.length();
minFile = file;
}
return;
}
if(file.isDirectory()){
File[] fs = file.listFiles();
if(null!=fs)
for (File f : fs) {
listFiles(f);
}
}
}
public static void main(String[] args) {
File f = new File("c:\windows");
listFiles(f);
}
- 流
–
输入流: InputStream (比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流)
输出流:OutputStream
-用于以字节的形式读取和写入数据
-数据源可以是文件,还可以是数据库,网络甚至是其他的程序
示例:
之前:
代码:
package file;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestFile {
public static void main(String[] args) {
//**输入流
try{
创建:
//创建文件。
File f = new File("d:/lol.txt");
// 创建基于文件的输入流。通过这个输入流,就可以把数据从硬盘,读取到Java的虚拟机中来,也就是读取到内存中
FileInputStream fis = new FileInputStream(f);
//创建字节数组,其长度就是文件的长度
byte[] b =new byte[(int)f.length()];
读取:
//以字节流的形式读取文件所有内容
fis.read(b);
for(byte i:b)
{
System.out.println(i);
}
//每次使用完流,都应该进行关闭
fis.close();
}catch(IOException e){
e.printStackTrace();
}
//**输出流
try {
准备:
// 准备文件lol2.txt(其中的内容是空的)
File f = new File("d:/lol2.txt");
// 创建基于文件的输出流
FileOutputStream fos = new FileOutputStream(f);
// 准备字节数组
byte data[] = { 88, 89 };
写出:
// 把数据写到输出流
fos.write(data);
// 关闭输出流
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
//注: 如果文件d:/lol2.txt不存在,写出操作会自动创建该文件。
//但是如果是文件 d:/xyz/lol2.txt,而目录xyz又不存在,会抛出异常
}
}
之后:
解析;A->65,B->66;88->X,89->Y.
-
改进:
以字节流的形式向文件写入数据,自动创建中间不存在的目录
try {
File f = new File("d:/xyz/abc/def/lol2.txt");
//因为默认情况下,文件系统中不存在 d:xyzabcdef,所以输出会失败
//首先获取文件所在的目录
File dir = f.getParentFile();
//如果该目录不存在,则创建该目录
if(!dir.exists()){
dir.mkdirs(); //使用mkdirs则会把不存在的目录都创建好
}
byte data[] = { 88, 89 };
FileOutputStream fos = new FileOutputStream(f);
fos.write(data);
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
则,会自动将中间的目录不存在的目录xyzabcdef都实体化创建好
-
练习:拆分文件
找到一个大于100k的文件,按照100k为单位,拆分成多个子文件,并且以编号作为文件名结束。
比如文件 eclipse.exe,大小是309k。
拆分之后,成为
eclipse.exe-0
eclipse.exe-1
eclipse.exe-2
eclipse.exe-3
拆分的思路,先把源文件的所有内容读取到内存中,然后从内存中挨个分到子文件里
提示,这里用到了数组复制Arrays.copyOfRange
package file;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
public class TestFile {
public static void main(String[] args) {
int eachsize=100*1024; //100k
//创建源文件及其相关量
File f = new File("d:/2.doc");
byte fileContent[]=new byte[(int)l];
long l=f.length();
System.out.println("源文件大小:"+l);
//先读取
通过输入流,将文件f的数据读进来
try {
FileInputStream fis=new FileInputStream(f);
fis.read(fileContent);
fis.close();
} catch (IOException e){
e.printStackTrace();
}
分批写出
int i=0;
int n=(int)l/eachsize+(int)((l%eachsize>0)?1:0); 计算需要被划分成多少份子文件
for(i=0;i<n;i++)
{
//准备:每一份子文件eachFile及其相关量
File eachFile=new File(f.getParent(),eachFileName);
String eachFileName=f.getName()+"-"+i;
byte eachContent[]=new byte[eachsize];
// 通过数组复制Arrays.copyOfRange。将源文件的内容复制部分数据到eachFile
if(i!=n-1) // 除开最后一个文件,其他文件大小都是100k
eachContent=Arrays.copyOfRange(fileContent,eachsize*i, eachsize*(i+1)-1);
else// 最后一个文件的大小是剩余的
eachContent=Arrays.copyOfRange(fileContent,eachsize*i, (int)l-1);
//写出:通过输出流,把eachfile里的数据写出去
try{
FileOutputStream fos=new FileOutputStream(eachFile);
fos.write(eachContent);
fos.close();
System.out.printf("输出子文件%s,其大小为%,d字节 %n",eachFileName,eachFile.length());
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
输出结果:
????文件及其相关量:
文件类型(File)
内容 (字节数组byte[ ]);
名字(String);
长度(long) …
????字节数组像是输入输出的纽带:流入的数据在那存着,流出的数据从它那去。
像是一个保存的容器:它存放保留着数据
是一个流的媒介:输入输出以字节的形式进行。
- 练习:拆分文件
不需要把所有的子文件都先读取到内存中,而是一边读取子文件的内容,一边写出到目标文件
package file;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.util.Arrays;
public class TestFile {
public static void main(String[] args) {
try {
//准备最后目标文件及其相关量
File f = new File("d:","合成2.doc");
FileOutputStream fos=new FileOutputStream(f);
//边读边出
int n=0; //子文件的数目
while(true)
{
判断是否进行本轮循环
File eachfile = new File("d:","2.doc"+"-"+n++);
if(!eachfile.exists()) 直到没有文件可以读
break;
读取子文件的内容
byte[] eachcontent=new byte[(int)eachfile.length()];
FileInputStream fis=new FileInputStream(eachfile);
fis.read(eachcontent);
fis.close();
把子文件的内容写出去
fos.write(eachcontent);
fos.flush(); //保证缓存清空输出
System.out.printf("把子文件 %s写出到目标文件中%n",eachfile);
}
fos.close();
System.out.printf("最后目标文件的大小:%,d字节" , f.length());
}catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
- 关闭流
–
- 在try中关闭,存在巨大的资源占用隐患。
- 在finally中关闭
FileOutputStream fos = null; // 为了在finally中关闭,声明在try外面
try{
fos= new FileOutputStream(f);
。。。。。。
}catch(IOException e){
e.printStackTrace();
}finally{
try{
if(fos!=null)
fos.close();
}catch(IOException e){
e.printStackTrace();
}
}
- 在try()中关闭
try (FileInputStream fis = new FileInputStream(f)) {
//把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
。。。。。。
} catch (IOException e) {
e.printStackTrace();
}
============================================================================================================================================
2.3
一、 I/O流(续上)
- 字符流
- Reader字符输入流:
Writer字符输出流
专门用于字符的形式读取和写入数据- 相较字节流。除三处改动外其余不变
1.字符数组的类型从byte变为char
2.流的创建变成了try()的形式(故无需关闭流)
在括号里边进行FileReader fr = new FileReader(f) 和 FileWriter fr = new FileWriter(f),
3.import java.io.FileReader; import java.io.FileWriter;
练习:文件加密
加密算法:
数字:如果不是9的数字,在原来的基础上加1,比如5变成6, 3变成4
如果是9的数字,变成0
字母字符:如果是非z字符,向右移动一个,比如d变成e, G变成H
如果是z,z->a, Z-A。字符需要保留大小写
非字母字符:比如’,&^ 保留不变,中文也保留不变
package file;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.util.Arrays;
public class TestFile {
public static void encodeFile(File encodingFile, File encodedFile){
char acontent[]=new char [(int)encodingFile.length()];
//先读如成为字符流
try(FileReader fr= new FileReader(encodingFile)){
fr.read(acontent);
//处理
for(int i=0;i<(int)encodingFile.length();i++){
acontent[i]=encodeChar(acontent[i]);
}
}catch(IOException e){
e.getStackTrace();
}
//写出
try(FileWriter fw= new FileWriter(encodedFile)){
fw.write(acontent);
}catch(IOException e){
e.getStackTrace();
}
}
public static char encodeChar(char c){
if(c>'0'&&c<='9'||c>'a'&&c<='z'||c>'A'&&c<='Z')
c--;
if(c=='0')
c='9';
if(c=='a')
c='z';
if(c=='A')
c='Z';
return c;
/*方二:
if (isLetterOrDigit(c)) {
switch (c) {
case '9':
c = '0';
break;
case 'z':
c = 'a';
break;
case 'Z':
c = 'A';
break;
default:
c++;
break;
}*/
}
public static void main(String[] args) {
//创建文本
File f=new File("d:/lol.txt");
//准备文件
File f2=new File("d:/","lol2.txt");
//调用
encodeFile(f,f2);
}
}
-
中文问题
- 不同的编码方式对应不同的棋盘
ISO-8859-1 ASCII 数字和西欧字母
GBK GB2312 BIG5 中文
UNICODE (统一码,万国码)
并派生了各种减肥子编码, 比如UTF-8对数字和字母就使用一个字节,而对汉字就使用3个字节- 以字符 中 为例,查看其在不同编码方式下的值是多少
也即在不同的棋盘上的位置
- 文件的编码方式
用记事本打开任意文本文件,并且另存为,就能够在编码这里看到一个下拉。
eclipse也有类似的编码方式,右键任意文本文件,点击最下面的"property"
就可以看到Text file encoding
- 用FileInputStream 字节流正确读取中文
-先把它读成字节流,再放到对应编码的棋盘上。这样会识别出对应字符 - 识别出对应的中文:new String(字节数组,“要放的编译棋盘”)
- 用FileReader 字符流正确读取中文
FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替,像这样:(在本例中,用记事本另存为UTF-8格式,然后用UTF-8就能识别对应的中文了。)
new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8"));
示例:
package Stream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
public class TestStream {
public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException {
File f = new File("d:\lol.txt");
System.out.println("默认编码方式:"+Charset.defaultCharset());
//FileReader得到的是字符,所以一定是已经把字节根据某种编码识别成了字符了
//而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK
try (FileReader fr = new FileReader(f)) {
char[] cs = new char[(int) f.length()];
fr.read(cs);
System.out.printf("FileReader会使用默认的编码方式%s,识别出来的字符是:%n",Charset.defaultCharset());
System.out.println(new String(cs));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替
//并且使用new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8")); 这样的形式
try (InputStreamReader isr = new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8"))) {//指定编码方式
char[] cs = new char[(int) f.length()];
isr.read(cs);
System.out.printf("InputStreamReader 指定编码方式UTF-8,识别出来的字符是:%n");
System.out.println(new String(cs));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
解释:什么中字前面有一个?
如果是使用记事本另存为UTF-8的格式,那么在第一个字节有一个标示符,叫做BOM用来标志这个文件是用UTF-8来编码的。
练习:移除BOM
如果用记事本根据UTF-8编码保存汉字就会在最前面生成一段标示符,这个标示符用于表示该文件是使用UTF-8编码的。
找出这段标示符对应的十六进制,并且开发一个方法,自动去除这段标示符。
//前情提要
C:projectj2sesrctest2.txt目录下存储的是记事本另存为UTF-8格式的txt文件,内容为“中”。
package Stream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
public class TestStream{
public static void main(String[] args) {
File f = new File("C:\project\j2se\src\test\2.txt");
try(FileInputStream fis=new FileInputStream(f)){
//读入
byte[]b=new byte[(int)f.length()];
fis.read(b);
//首先确认按照UTF-8识别出来有?
String str=new String(b,"UTF-8");
System.out.println("init:"+str);
//打印出文件里所有的数据的16进制
for(byte bb:b){
int i=bb&0xff;
System.out.print(Integer.toHexString(i)+" ");
}
System.out.println();
//根据前面的所学,知道'中'字对应的UTF-8编码是:e4 b8 ad
//通过观察法得出 UTF-8的 BOM 是 ef bb bf
//去掉BOM
byte[]bom=new byte[3];
bom[0]=(byte)0xef;
bom[1]=(byte)0xbb;
bom[2]=(byte)0xbf;
byte[]withoutbom=Arrays.copyOfRange(b, bom.length,b.length);
//打印出去掉了BOM之后的数据的16进制
for(byte ww:withoutbom){
int i=ww&0xff;
System.out.print(Integer.toHexString(i)+" ");
}
System.out.println();
//将数据识别成中文,并输出
String str2=new String(withoutbom,"UTF-8");
System.out.println("final:"+str2);
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("出错了");
e.printStackTrace();
}
}
}
-
缓存流
- 缓存字符输入流BufferedReader
可以一次读取一行数据
// 缓存流必须建立在一个存在的流的基础上
//故先创建文件字符流
try(FileReader fr =new FileReader(f); BufferedReader br=new BufferedReader(fr)){
while(true){
//一行一行读入
String str=br.readLine();
//直至没有
if(str==null)
break;
System.out.println(str);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
- 缓存字符输出流PrintWriter
可以一次写出一行数据
// 缓存流必须建立在一个存在的流的基础上
// 故先创建文件字符流
try(FileWriter fw =new FileWriter(f); PrintWriter pw =new PrintWriter(pw)){
// 向文件中输出(写入)三行语句
pw.println("兵长!");
pw.println("下一集!");
pw.println("出现啦!");
//通过pw.write输出也可以
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
- flush
> 需要立即把输出(数据写入到硬盘),而不是等缓存满了才写出去。
pw.println("兵长!");
//强制把缓存中的数据写入硬盘,无论缓存是否已满
pw.flush();
练习:移除注释
设计一个方法,用于移除Java文件中的注释
public void removeComments(File javaFile)
比如,移出以//开头的注释行
public static void removeComments(File javaFile) {
StringBuffer sb = new StringBuffer();
//读取内容
try (FileReader fr = new FileReader(javaFile); BufferedReader br = new BufferedReader(fr);) {
while (true) {
String line = br.readLine();
if (null == line)
break;
//如果不是以//开头,就保存在StringBuffer中
if (!line.trim().startsWith("//"))//trim去头尾多余空格
sb.append(line).append("rn");
}
} catch (IOException e) {
e.printStackTrace();
}
try (
FileWriter fw = new FileWriter(javaFile);
PrintWriter pw = new PrintWriter(fw);
) {
//写出内容
pw.write(sb.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
-
数据流
文件与数据 (int类,boolean类,String类) - DataInputStream 数据输入流
DataOutputStream 数据输出流
try (
FileInputStream fis = new FileInputStream(f);
DataInputStream dis =new DataInputStream(fis);
){
boolean b= dis.readBoolean();
int i = dis.readInt();
String str = dis.readUTF();
。。。
}。。。
try (
FileOutputStream fos = new FileOutputStream(f);
DataOutputStream dos =new DataOutputStream(fos);
){
dos.writeBoolean(true);
dos.writeInt(300);
dos.writeUTF("123 this is gareen");
。。。
}。。。
-
对象流
文件与对象Object
注:把一个对象序列化有一个前提是:这个对象的类,必须实现了Serializable接口
下例:对象是Hero
则TestStream.java
try(
//创建对象输出流
FileOutputStream fos = new FileOutputStream(f);
ObjectOutputStream oos =new ObjectOutputStream(fos);
//创建对象输入流
FileInputStream fis = new FileInputStream(f);
ObjectInputStream ois =new ObjectInputStream(fis);
) {
oos.writeObject(h);
Hero h2 = (Hero) ois.readObject();
。。。
}。。。
且Hero.java
package charactor;
import java.io.Serializable;
public class Hero implements Serializable {
//表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号
private static final long serialVersionUID = 1L;
public String name;
public float hp;
}
ADD:
准备一个长度是10,类型是Hero的数组, 使用10个Hero对象初始化该数组
//创建Hero数组
Hero h[] =new Hero[10];
for (int i = 0; i < h.length; i++) {
h[i] = new Hero(“hero:” +i);
}
另 ,Hero.java中需要构造
public Hero(String str){
this.name=str;
}
-
控制台流
System.out 是常用的在控制台输出数据的
System.in 可以从控制台输入数据
//System.in 输入
import java.io.InputStream;
try (InputStream is = System.in;) {
while (true) {
// 敲入a,然后敲回车可以看到
// 97 13 10
// 97是a的ASCII码
// 13 10分别对应回车换行
int i = is.read();
System.out.println(i);
}
} catch...
//Scanner输入
import java.util.Scanner;
main:
Scanner s = new Scanner(System.in);
while(true){
String line = s.nextLine();
System.out.println(line);
}
综上:
==================================================================================================================================================================
2.4
一、结构框架
为了解决数组的局限性,引入容器类的概念。
容器的容量"capacity"会随着对象的增加,自动增长
只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。
-
容器类ArrayList
- 常用方法
- 常用方法
package collection;
import java.util.ArrayList;
import character.Hero;
public class TestCollection {
public static void main(String[] args) {
//初始化五个对象
//注意:ArrayList类里的每一个对象都是Hero类型,add的也是Hero类型,而不是一个name相同的字符串
ArrayList a= new ArrayList();
for(int i=0;i<5;i++)
a.add(new Hero("hero"+i));
//add
Hero specialhero=new Hero("special hero");
a.add(3,specialhero);
//整个容器打印
System.out.println(a);
//contains
//判断标准是对象是否相同,而非对象的name值是否相等
System.out.println("1:"+a.contains(new Hero("hero1")));//虽然一个新的对象名字也叫 hero 1,但是contains的返回是false
System.out.println("2:"+a.contains("hero1"));
System.out.println("3:"+a.contains(specialhero));//而对specialHero的判断,contains的返回是true
//get(基0)
System.out.println("4:"+a.get(5));
//System.out.println("5:"+a.get(6)); 如果超出了范围,会报错
//indexOf
System.out.println("6:"+a.indexOf(specialhero));
System.out.println("7:"+a.indexOf(new Hero("hero1"))); //没有它,返回-1
//remove
a.remove(2); //删除下标是2的对象
System.out.println(a);
a.remove(specialhero); //删除special hero
System.out.println(a);
//size
System.out.println(a.size());
//set
a.set(2, new Hero("hero2"));
System.out.println("8:"+a);
//toArray
Hero[]h =(Hero[])a.toArray(new Hero[]{});
System.out.println("9:" +h);
//把另一个容器里所有的元素,都加入到该容器里来
ArrayList anotherHeros = new ArrayList();
anotherHeros.add(new Hero("hero a"));
anotherHeros.add(new Hero("hero b"));
anotherHeros.add(new Hero("hero c"));
a.addAll(anotherHeros);
System.out.println("10:" +a);
//clear
a.clear();
System.out.println("11:" +a);
}
}
练习-判断集合里是否存在一个 name等于 "hero 1"的对象
ADD:
for (int i = 0; i < heros.size(); i++) {
Hero h = (Hero) heros.get(i);
if(name.equals(h.name ) ){
System.out.printf("找到了name是%s的对象",name);
break;
}
}
关于equals
如果来自同一个类的两个对象,如果没有重写equals方法的逻辑,其结果和equals结果是相同的(是否是同一引用),如果有重写equals方法,则和equals结果可能会不同。
如:Integer就重写了equals,所以其中调用equals表示的是其值是否相同。
- list接口
import java.util.List;
ArrayList实现了接口List
常用方式接口类引用,指向实例化对象
List heros = new ArrayList();
- 泛型 Generic
指定了泛型的容器,只能存放指定类型的元素以及其子类,比如都是Hero类,或都是Item类
//引入泛型Generic
//声明容器的时候,就指定了这种容器,只能放Hero,放其他的就会出错
List<Hero> genericheros = new ArrayList<Hero>(); //也可以简写成new ArrayList<>();
genericheros.add(new Hero("盖伦"));
//如果不是Hero类型,根本就放不进去
//genericheros.add(new Item("冰杖"));
//除此之外,还能存放Hero的子类
genericheros.add(new APHero());
//并且在取出数据的时候,不需要再进行转型了,因为里面肯定是放的Hero或者其子类
Hero h = genericheros.get(0);
练习-设计一个ArrayList,使得这个ArrayList里,又可以放Hero,又可以放Item,但是除了这两种对象,其他的对象都不能放
首先创建一个接口 LOL,不需要在其中声明任何方法
接着让Hero和Item都实现LOL接口
最后,声明一个ArrayList的变量lolList,它的泛型是
List lolList = new ArrayList<>();
这样在lolList中就即放Hero对象,又放Item对象了。
- 迭代器遍历
//使用迭代器
//使用while的iterator
Iterator<Hero> it= heros.iterator();
//从最开始的位置判断"下一个"位置是否有数据
//如果有就通过next取出来,并且把指针向下移动
//直到"下一个"位置没有数据
while(it.hasNext()){
Hero h = it.next();
System.out.println(h);
}
//使用for的iterator
for (Iterator<Hero> iterator = heros.iterator(); iterator.hasNext();) {
Hero hero = (Hero) iterator.next();
System.out.println(hero);
}
===========================================================================================================================
2.5
一、其他集合
-
LinkedList
LinkedList 实现了List接口
- import java.util.LinkedList;
- import java.util.List;
LinkedList 实现了双向链表结构Deque接口(队列)
- import java.util.LinkedList;
- LinkedList ll =new LinkedList();
- get/removeFirst/Last()
LinkedList 实现了先进先出Queue接口(队列)
-import java.util.LinkedList;
import java.util.Queue;
- Queue q= new LinkedList();
- offer 在最后添加元素
- poll 取出第一个元素
- peek 查看第一个元素
Deque 继承 Queue,间接的继承了 Collection -
二叉树
eg:假设通过二叉树对如下10个随机数进行排序
67,7,30,73,10,0,78,81,10,74
第一步:插入数据
插入基本逻辑是,小、相同的放左边,大的放右边
-第二步是遍历
我们希望遍历后的结果是从小到大的,所以应该采用中序遍历
左序即: 中间的数遍历后放在左边
中序即: 中间的数遍历后放在中间
右序即: 中间的数遍历后放在右边
package collection;
import java.util.ArrayList;
import java.util.List;
public class Node{
public Node leftnode;
public Node rightnode;
public Object value;
//插入数据
public void add(Object v){
if(this.value==null)
this.value=v;
else{
if((Integer)v-(Integer)value<=0){
if(leftnode==null)
leftnode=new Node();
leftnode.add(v);
}
if((Integer)v-(Integer)value>0){
if(rightnode==null)
rightnode=new Node();
rightnode.add(v);
}
}
}
//遍历
public List<Object> traversal(){
//容器类t里存放的是所有节点的value。
List<Object> t = new ArrayList<>();
//中序遍历(按照一定的顺序add到t里)
if(this.leftnode!=null)
t.addAll(this.leftnode.traversal());
t.add(value);
if(this.rightnode!=null)
t.addAll(this.rightnode.traversal());
return t;
}
public static void main(String[] args) {
int a[]=new int []{67, 7, 30, 73, 10, 0, 78, 81, 10, 74};
Node n =new Node();
for(int i:a){
n.add(i);
}
System.out.println(n.traversal());
}
}
-
HashMap
储存数据的方式是—— 键值对
- import java.util.HashMap;
- 初始化。put。get。
eg:HashMap<String,String> dictionary = new HashMap<>();
dictionary.put(“adc”, “物理英雄”);
dictionary.put(“adc”);
对于HashMap而言,key是唯一的,不可以重复的。
所以,以相同的key 把不同的value插入到 Map中会导致旧元素被覆盖,只留下最后插入的元素。
不过,同一个对象可以作为值插入到map中,只要对应的key不一样
练习-查找内容性能比较
准备一个ArrayList其中存放3000000(三百万个)Hero对象,其名称是随机的,格式是hero-[4位随机数]
hero-3229
hero-6232
hero-9365
…
因为总数很大,所以几乎每种都有重复,把名字叫做 hero-5555的所有对象找出来
要求 借助HashMap,找出结果,并统计花费的时间
package collection;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import character.Hero;
public class TestCollection {
public static void main(String[] args) {
//初始化Hero对象
List<Hero> h =new ArrayList<>();
for (int i = 0; i < 3000000; i++) {
Hero hh = new Hero( "hero-" + ((int)(Math.random()*9000)+1000));
h.add(hh);
}
//初始化MAP(键值对)(就像字典)
HashMap<String,List<Hero>> m = new HashMap();
for(Hero hh:h){//遍历不同的值
//(某值对应着某键) 通过键值对,先获得这个键已有的值集合(就像容器)
List <Hero>list=m.get(hh.name);
if(list==null){//集合不存在则创建新的集合,并加到字典里
list = new ArrayList<>();
m.put(hh.name, list);
}
list.add(hh);//在值集合上加上该值
}
//用MAP查找
long start=System.currentTimeMillis();
//通过键值对,获得目标键对应的值集合
List<Hero> aimed=m.get("hero-5555");
long end=System.currentTimeMillis();
System.out.println("目标大小:"+aimed.size());
System.out.println("耗时:"+(int)(end-start));
}
}
-
HashSet
不能重复!
常同于统计”某字符串数组里重复的字符串有多少种“,因为/第二次插入同样的数据,是插不进去的,容器中只会保留一个
没有按照元素的插入顺序排列!
- import java.util.HashSet;
- HashSet numbers = new HashSet();
- HashSet names = new HashSet();
- add✔ get× 遍历需要用到迭代器,或者增强型for循环
HashSet自身并没有独立的实现,而是在里面封装了一个Map.
HashSet是作为Map的key而存在的
而value是一个命名为PRESENT的static的Object对象,
ADD:equalsIgnoreCase与equals区别是前者不区分大小写,而后者区分
-
Collection
- 是个接口
set不重复,无顺序;list有有,Deque 继承 Queue,间接的继承了 Collection
- 是个接口
-
Collections
-是一个类,容器的工具类
import java.util.Collections;
package collection;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
public class TestCollection{
public static void main(String[] args) {
//初始化集合n
List<Integer> n=new ArrayList<>();
for (int i = 0; i < 10; i++) {
n.add(i);
}
//输出集合中的数据
System.out.println(n);
//翻转reverse
Collections.reverse(n);
System.out.println(n);
//混淆shuffle
Collections.shuffle(n);
System.out.println(n);
//排序sort
Collections.sort(n);
System.out.println(n);
//交换swap(基0)
Collections.swap(n,0,5);
System.out.println(n);
//滚动rotate
Collections.rotate(n,2);
System.out.println(n);
//线程安全化synchronizedList
//把非线程安全的List转换为线程安全的List
List<Integer> synchronizeda=(List<Integer>)Collections.synchronizedList(n);
}
}
ADD:
List类的numbers前3位出现3 1 4的代码语言为
(numbers.get(0)==3&&numbers.get(1)==1&&numbers.get(2)==4)
最后
以上就是忧虑自行车为你收集整理的寒假宅喵java进阶upup篇2.12.22.3练习:移除BOM2.4一、结构框架2.5的全部内容,希望文章能够帮你解决寒假宅喵java进阶upup篇2.12.22.3练习:移除BOM2.4一、结构框架2.5所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复