一、什么是API
API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。
简单来说:API就是别人编写好的程序/代码,我们直接拿来用,就叫做API,我们使用了别人代码(或者程序)中的某个函数、类、对象,就叫做使用了某个API
常用API结构:
java给我们提供了很多了不同用途的API,据说如果你能够掌握其中的5%的API,那么你就已经是大佬了,所以我们肯定学习不完,我这篇博客给讲下我们常用的API有哪些。
二、文档注释
用处:代码是写给人看的,写注释是为了能让人快速看懂代码,方便程序员间的交流。代码要有规范,要有良好的风格,要有很好的可读性,合理的注释是提高代码可读性的方法之一
单行注释: //
以 // 开头, // 后面的内容都会被认为是注释
多行注释: /**/
以 /* 开头,以 / 结束, / 和 */ 之间的的内容都会被认为是注释。
多行注释用于说明比较复杂的内容,如复杂的逻辑或者算法
文档注释: 它以 /** 开始,以 */结束,文档注释允许你在程序中嵌入关于程序的信息,有了这个注释就可以使用 javadoc 工具软件来生成信息,并输出到HTML文件中
格式:
/**
* .........
* .........
*/
在开始的 /** 之后,第一行或几行是关于类、变量和方法的主要描述
之后,你可以包含一个或多个各种各样的 @ 标签。每一个 @ 标签必须在一个新行的开始或者在一行的开始紧跟星号(*),其中常用的标签如下:
栗子:
/**
* @author 作者 天子笑
*/
public class ApiDocDemo {
/**
* sayHello中使用的问候语
*/
public static final String INFO = "你好!";
/**
* 对指定用户添加问候语
* @param name 给定的用户名
* @return 含有问候语的字符串
*/
public String sayHello(String name){
return "你好!"+name;
}
}
为什么有了单行注释和多行注释,还要文档注释?
答:文档注释可以提取出来,生成像官方API帮助文档风格一样的文档,,因为我们工作做大型项目,是多人合作,则文档注释可以让别人明了你项目每一步都在干嘛!
使用JDK提供的javadoc命令,可以自动生成一份HTML格式的文档,风格和官方API帮助文档一样
使用javadoc命令提取文档:
javadoc -d doc -encoding UTF-8 -charset UTF-8 xxx.java
这条命令的意思就是把xxx.java中的文档注释提取出来,生成的帮助文档放在doc文件夹下,其编码格式为UTF-8
三、String字符串
对于String类我们并不陌生,经常使用,关于字符串的操作方法我们是不需要像其他操作一样手动导包的,其String和StringBuffer等类封装在java.lang包中,我们直接调用字符串方法即可。String类为我们提供了很多方法(API),所以我们在操作字符串的时候用这些方法是很便捷的。
这里我只讲解String常用方法,想更深入了解String类的,可以看我这篇博客:源码刨析String ,希望可以对你有所帮助
1.String基本操作方法
首先说一下基本操作方法,字符串的基本操作方法中包含以下几种:
(1)获取字符串长度length()
格式:int length = str.length();
(2)获取字符串中的第i个字符charAt(i)
格式:char ch = str.charAt(i); //i为字符串的索引号,可得到字符串任意位置处的字符,保存到字符变量中
(3)获取指定位置的字符方法getChars(4个参数)
格式:char array[] = new char[80]; //先要创建以一个容量足够大的char型数组,数组名为array
str.getChars(indexBegin,indexEnd,array,arrayBegin);
解释一下括号中四个参数的指向意义:
1.indexBegin:需要复制的字符串的开始索引
2. indexEnd: 需要复制的字符串的结束索引,indexEnd-1
3.array: 前面定义的char型数组的数组名
4.arrayBegin:数组array开始存储的位置索引号这样我们就可以将字符串中想要的范围内的字符都复制到字符数组中,将字符数组打印输出即可。
与getChars()类似的方法有一个getBytes(),两者使用上基本相同,只是getBytes()方法创建的是byte类型的数组,而byte编码是默认字符集编码,它是用编码表示的字符。
栗子:
//String类基本操作方法
public class Demo1 {
public static void main(String args[]){
String str = "一朵菊花,一个小鬼,就凭你们也敢欺负我的儿子"; //定义一个字符串
System.out.println(str); //输出字符串
/***1、length()方法***/
int length = str.length();//得到字符串长度
System.out.println("字符串的长度为:"+length);
/***2、charAt()方法***/
char ch = str.charAt(7); //得到索引为7的字符
System.out.println("字符串中的第8个字符为:"+ch);
/***3、getChars()方法***/
char chardst[] = new char[80]; //定义容量为80的字符数组,用于存储从字符串中提取出的一串字符
str.getChars(0,14,chardst,0);
//System.out.println("字符数组中存放的内容为:"+chardst);//错误,输出的是编码
System.out.println(chardst); //**括号中不可带其他字符串
}
}
运行结果:
一朵菊花,一个小鬼,就凭你们也敢欺负我的儿子
字符串的长度为:22
字符串中的第8个字符为:小
一朵菊花,一个小鬼,就凭你们
2.字符串比较
对于字符串比较的一些比较方法,大家可以看下我这篇博客:java比较方法对比,希望对你有所帮助
言归正传,我们知道对于基本上数据类型我们可以用“==”来判断大小,但是我们知道 “= =” 判断引用类型数据是否相等,比较的是堆内存地址,但是我们判断字符串是否相等,比较的是内容是否相等,所以不能用,所以java给我们提供了以下方法:
(1)不忽略字符串大小写情况下字符串的大小比较方法compareTo(another str)
格式:int result = str1.compareTo(str2)
(2) 忽略字符串大小写情况下字符串的大小比较方法compareTOIgnoreCase(another str)
格式:int result = str1.compareToIgnoreCase(str2);
(3)不忽略字符串大小写情况下判别字符串相等的方法eaquals(another str)
格式:boolean result = str1.equals(str2);
当且仅当str1和str2的长度相等,且对应位置字符的Unicode编码完全相等,返回true,否则返回false
(4) 忽略字符串大小写情况下判别字符串相等的方法equalsIgnoreCase(another str)
格式:boolean result = str1.equals(str2);
这里有个误区: 可能大家看上面equals方法可以用来判断字符串内容,就说:equals是用来比较数据内容的,那么是不是这样呢?我们看看Object 里面的equals源码:
public boolean equals(Object obj) {
return (this==obj);
}
我们看到Object里面的equals方法还是用“==”来比较的,问题来了,那为什么String里面可以用equals呢?真相只有一个那就是:String重写了equals方法,源码如下:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
栗子:
public class Demo2{
public static void main(String args[]){
String str1 = "A";
String str2 = "a";
String str3 = "Hello";
String str4 = "hello";
/***1、compareTo方法***/
//不忽略字符串字符大小写
int a = str1.compareTo(str2);
System.out.println(a);
/***2、compareToIgnoreCase()方法***/
//忽略字符串字符大小写
int b = str1.compareToIgnoreCase(str2);
System.out.println(b);
/***3、equals()方法***/
//不忽略字符串字符大小写
if(str3.equals(str4)){
System.out.println(str3+"="+str4);
}else{
System.out.println(str3+"!="+str4);
}
/***4、equalsIgnoreCase()方法***/
//忽略字符串字符大小写
if(str3.equalsIgnoreCase(str4)){
System.out.println(str3+"="+str4);
}else{
System.out.println(str3+"!="+str4);
}
}
}
运行代码:
-32
0
Hello!=hello
Hello=hello
3. 字符串与其他数据类型的转换
由于转换方法太多,整理成表:
栗子:
public class Demo3 {
public static void main(String args[]){
/***将字符串类型转换为其他数据类型***/
boolean bool = Boolean.getBoolean("false"); //字符串类型转换为布尔类型
int integer = Integer.parseInt("20"); //字符串类型转换为整形
long LongInt = Long.parseLong("1024"); //字符串类型转换为长整形
float f = Float.parseFloat("1.521"); //字符串类型转换为单精度浮点型
double d = Double.parseDouble("1.52123");//字符串类型转换为双精度浮点型
byte bt = Byte.parseByte("200"); //字符串类型转换为byte型
char ch = "唐三".charAt(0);
/***将其他数据类型转换为字符串类型方法1***/
String strb1 = String.valueOf(bool); //将布尔类型转换为字符串类型
String stri1 = String.valueOf(integer); //将整形转换为字符串类型
String strl1 = String.valueOf(LongInt); //将长整型转换为字符串类型
String strf1 = String.valueOf(f); //将单精度浮点型转换为字符串类型
String strd1 = String.valueOf(d); //将double类型转换为字符串类型
String strbt1 = String.valueOf(bt); //将byte转换为字符串类型
String strch1 = String.valueOf(ch); //将字符型转换为字符串类型
}
}
4. 字符串查找
字符串查找无非分为两类:查找字符串和查找单个字符,而查找又可分为查找对象在字符串中第一次出现的位置和最后一次出现的位置,再扩展一步,我们可以缩小查找范围,在指定范围之内查找其第一次或最后一次出现的位置
== 查找字符/字符串出现的位置==
1、indexOf()方法
str.indexOf(ch):返回指定字符在字符串中第一次出现位置的索引
str.indexOf(ch,fromIndex):回指定索引位置之后第一次出现该字符的索引号
2、lastIndexOf()方法
str.lastIndexOf(ch):返回指定字符在字符串中最后一次出现位置的索引
str.lastIndexOf(ch,fromIndex):返回指定索引位置之前最后一次出现该字符的索引号
栗子:
public class Demo3{
public static void main(String args[]){
String str = "How qi bocome handsome like qi ge"; //定义一个长字符串
System.out.println("该字符串为:"+str);
/***1、indexOf()方法查找字符首个出现位置格式1,2***/
int index1 = str.indexOf(" "); //找到第一个空格所在的索引
int index2 = str.indexOf(" ",4); //找到索引4以后的第一个空格所在索引
System.out.println("第一个空格所在索引为:"+index1);
System.out.println("索引4以后的第一个空格所在索引为:"+index2);
System.out.println("*****************");
/***2、lastIndexOf()方法查找字符最后出现位置格式1,2***/
int index3 = str.lastIndexOf(" "); //找到最后一个空格所在的索引
int index4 = str.lastIndexOf(" ",10);//找到索引10以后的第一个空格所在索引
System.out.println("最后一个空格所在索引为:"+index3);
System.out.println("索引10以前最后一个空格所在索引为:"+index4);
System.out.println("*****************");
/***3、indexOf()方法查找子字符串第一次出现位置格式1,2***/
int index5 = str.indexOf("qi"); //找到"qi"子字符串第一次出现位置的索引
int index6 = str.indexOf("qi",5);//找到索引5以后子字符串"qi"第一个出现位置所在索引
System.out.println("子字符串qi第一次出现位置的索引号为:"+index5);
System.out.println("索引5以后子字符串qi第一次出现位置的索引号为:"+index6);
System.out.println("*****************");
/***4、lastIndexOf()方法查找子字符串最后一次出现位置格式1,2***/
int index7 = str.lastIndexOf("qi");
int index8 = str.lastIndexOf("qi",5);
System.out.println("子字符串qi最后一次出现位置的索引号为:"+index7);
System.out.println("索引号5以后子字符串qi最后一次出现位置的索引号为:"+index8);
}
}
运行结果:
该字符串为:How qi bocome handsome like qi ge
第一个空格所在索引为:3
索引4以后的第一个空格所在索引为:6
*****************
最后一个空格所在索引为:30
索引10以前最后一个空格所在索引为:6
*****************
子字符串qi第一次出现位置的索引号为:4
索引5以后子字符串qi第一次出现位置的索引号为:28
*****************
子字符串qi最后一次出现位置的索引号为:28
索引号5以后子字符串qi最后一次出现位置的索引号为:4
5. 截取与拆分
这类方法是截取出一个长字符串中的一个子字符串或将字符串按照正则表达式的要求全部拆分保存到一个字符串数组中。具体方法如下所示:
1.截取方法substring()方法
格式1:String result = str.substring(index);
格式2:String result = str.substring(beginIndex,EndIndex);//实际索引号[beginIndex,EndIndex-1]
2.split()方法拆分
格式1 :String strArray[] = str.split(正则表达式);// 拆分的结果保存到字符串数组中
格式2:String strArray[] = str.split(正则表达式,limit);
6.替换或修改
1.concat()方法:合并字符串
格式:String result = str1.concat(str2); //将str1和str2合并
- toLowerCase()方法:将字符全部转化为小写
格式: String result = str.toLowerCase();
3.toUpperCase()方法: 将字符全部转化为大写
格式:String result = str.toUpperCase();
4.replaceAll()、replaceFirst()方法:需要匹配正则表达式
栗子:
public class Demo5{
//字符串替换与修改
public static void main(String args[]){
String str1 = "vbasic";
String str2 = "Vbasic";
System.out.println("str1 = "+str1);
System.out.println("str2 = "+str2);
/***1、concat()方法将两字符串合并***/
String str3 = str1.concat(str2);
System.out.println("str1和str2合并后的字符串为:"+str3);
/***2、toLowerCase()方法将str1字符全部转换为小写***/
String str4 = str1.toLowerCase();
System.out.println("str1的字符全部转换为小写:"+str4);
/***3、toUpperCase()方法将str2字符全部转换为大写***/
String str5 = str2.toUpperCase();
System.out.println("str2的字符全部转换为大写:"+str5);
}
}
运行结果:
str1 = vbasic
str2 = Vbasic
str1和str2合并后的字符串为:vbasicVbasic
str1的字符全部转换为小写:vbasic
str2的字符全部转换为大写:VBASIC
四、正则表达式
1、什么是正则表达式
正则表达式能够描述字符串的格式,通常的用于验证字符串内容,正则表达式并不是java语言的内容,而是独立于编程语言的体系,java觉得好,就把人家的拿过来用了
2.为什么需要正则表达式
在人机互动的时代,可以让计算机验证用户输入内容时候匹配,比如:手机号,身份证号,Email
注意:正则表达式只是用来验证字符串格式的,而不管字符串的有效性
比方说:我们知道qq邮箱格式:数字[email protected],正则表达式只匹配这个格式,而不管你给我的这个qq是不是有效的,被注销什么,一概不管!
3. 正则表达式字符说明
怎么用呢,我们来写几个栗子:
4.正则表达式应用
案例一:验证Email
方法:boolean matches(String regex)
使用正则表达式验证当前字符串格式,匹配通过返回true,否则返回false
public class MatchesDemo {
public static void main(String[] args) {
/**
* TODO: 1、String matches(String str)匹配
*/
String str = "[email protected]";
/*
[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\.[a-z-A-Z]+)+
*/
String regex = "[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\\.[a-z-A-Z]+)+";
boolean flag = str.matches(regex);
System.out.println(flag);//true
}
}
案例二:字符串拆分
方法:String【】split(String str)
将当前的字符串按照满足条件的正则表达式进行拆分
public class MatchesDemo {
public static void main(String[] args) {
/**
* TODO:2、String[]split(String regex)拆分字符串
* 注意:如果字符串最开始就遇到拆分项,那么拆分就会开始拆分出一个空的字符串;
* 如果连续遇到两个拆分字符串,则中间会拆分出一个空的字符串
* 如果在末尾遇到拆分项,拆分的空字符串被被忽略
*/
String st = "..asa.vashv1256vg.svs456....";
//拆出所有字母意思
// String[] arr = st.split("[0-9]+");
// String[]arr = st.split(".");
String[]arr = st.split("\\.");
System.out.println(arr.length);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
输出结果:
asa
vashv1256vg
svs456
注意: 如果字符串最开始就遇到拆分项,那么拆分就会开始拆分出一个空的字符串;如果连续遇到两个拆分字符串,则中间会拆分出一个空的字符串;如果在末尾遇到拆分项,拆分的空字符串被被忽略
案例三:替换字符串
方法:String replaceAll(String regex,String str)
将当前的字符串中满足正则表达式的部分替换为给定内容
/**
* TODO: replaceAll(String regex,String str)
* 将满足条件的字符串部分全部替换为给定内容
*/
String s3 = "adasc1234gvava89vd";
s3 = s3.replaceAll("[0-9]+","***");
System.out.println(s3);
s3 = s3.replaceAll("[a-z]+","+");
System.out.println(s3);
System.out.println("-----------------");
String s4 = "(cnm|sb)";
String s5 = "sb!我cnm";
s5 = s5.replaceAll(s4,"**");
System.out.println(s5);
这个用正则表达式替换字符串的的方法,对于喜欢玩游戏的人来说是经常间接的遇到的,因为玩游戏不爽的时候,就会喷人,但是屏幕把那些脏字全部转换成:* ;没错,程序底层就是采用的这个方法欧!
五、包装类
1.什么是包装类
每个基本数据类型都对应一个包装类,包装类就是把基本数据类型包装成对象
2.为什么需要包装类
基本数据类型不能直接参与引用类型的使用/计算,使用包装类可以将基本数据类型转换为引用数据类型,可以参与到其他引用的使用
3.包装类的使用
1、包装类:为了解决基本数据类型不能直接面向对象开发,提供的对应的引用类型
包装类就是以“对象形式”表示基本数据
2、java推荐使用包装类的静态方法:valueOf()形式创建包装类对象,
Integer在使用valueOf形式创建对象会重用【-128,127】之间的整数对象,减少内存开销
/**
* 1、包装类:为了解决基本数据类型不能直接面向对象开发,提供的对应的引用类型
* 包装类就是以“对象形式”表示基本数据
* 2、java推荐使用包装类的静态方法:valueOf()形式创建包装类对象,
* Integer在使用valueOf形式创建对象会重用【-128,127】之间的整数对象,减少内存开销
*/
public class Demo1 {
public static void main(String[] args) {
// TODO:Integer
//-128 ~127
Integer i1 = Integer.valueOf(123);
Integer i2 = Integer.valueOf(123);
System.out.println(i1==i2);
System.out.println(i1.equals(i2));
System.out.println("-----------------");
Integer i3 = new Integer(123);
Integer i4 = new Integer(123);
System.out.println(i3==i4);
System.out.println(i3.equals(i4));
//TODO: Long
Long l1 = Long.valueOf(123);
Long l2 = Long.valueOf(123);
System.out.println(l1==l2);
System.out.println(l1.equals(l2));
//TODO:Character
Character c1 = Character.valueOf('a');
Character c2 = Character.valueOf('a');
System.out.println(c1==c2);
System.out.println(c1.equals(c2));
System.out.println("---------------");
//TODO: Integer-->Double
Double d1 = i1.doubleValue();
System.out.println(d1);
}
}
包装类的常用功能
1、数字型包装类提供了:MAX_VALUE和MIN_VALUE 最大值和最小值
2、paseInt(String s): s–>int 别的包装类类似
3、JDK1.5推出了了一个新特性:自动拆装箱。该特性是编译器认可而非虚拟机,可以让
我们在写程序时,不考虑基本数据类型和对应包装类之间的转换问题,它帮我们做了
public class Demo2 {
public static void main(String[] args) {
int max = Integer.MAX_VALUE;// 0x7fffffff
// 0111 11111
int min = Integer.MIN_VALUE;//0x80000000
System.out.println(max+" "+min);
System.out.println(Integer.toBinaryString(0x7fffffff));
System.out.println(Integer.toBinaryString(0x80000000));
System.out.println("---------------------------");
Integer a = Integer.parseInt("123456");
System.out.println(a);
Double d = Double.parseDouble("123.456");
System.out.println(d);
//TODO:这里触发自动拆箱,编译器会补充代码改为:int i = new Integer(2).intValue();
int i = new Integer(2);
}
}
六、File类
1.什么是File类
java.io.File类就是文件的意思
java中File类对象用于表示硬盘上的一个文件夹/文件
2.为什么需要File类
我们java程序中可能需要控制硬盘上的文件
使用File类中各种方法可以获得文件/文件夹的信息
3.使用File类获得文件信息
使用File可以:
1、访问其表示的文件/目录属性(文件名、大小)
2、操作文件/目录(创建/删除)
3、访问一个目录的子项内容
栗子:
public class FileDemo {
public static void main(String[] args) {
File file = new File("./demo.txt");
String name = file.getName();
System.out.println("文件名:"+name);
Long len = file.length();
System.out.println("文件大小:"+len+"字节");
boolean cr = file.canRead();
boolean wr = file.canWrite();
System.out.println("可读:"+cr);
System.out.println("可写:"+wr);
boolean isHidden = file.isHidden();
System.out.println("是否隐藏:"+isHidden);
}
}
4. 创建文件
方法:createNewFile()
/**
* 使用File新建文件
*/
public class CreateNewFileDemo {
public static void main(String[] args) throws IOException {
//当前目录下创建一个:test.txt文件
File file = new File("./test.txt");
if (file.exists()){
//判断文件是否存在
System.out.println("文件存在");
}else {
//创建当前的file表示文件
file.createNewFile();
}
}
}
5. 删除文件
方法:delete()
;
/**
* 删除一个文件
*/
public class DeleteFile {
public static void main(String[] args) throws IOException, InterruptedException {
File file = new File("./test.txt");
//将当前目录下的:test.txt 文件删除
if (file.exists()){
file.deleteOnExit();
System.out.println("文件删除成功");
}else {
System.out.println("文件不存在");
}
}
}
6.创建目录
1、方法:mkdir() 创建一个空目录
;
/**
* 创建一个目录
*/
public class MkDirDemo {
public static void main(String[] args) {
//在当前目录下创建一个新的目录:demo
File file = new File("demo");
if (file.exists()){
System.out.println("该目录存在");
}else {
file.mkdir();
System.out.println("该目录创建成功");
}
}
}
2、方法:mkdirs()创建多级目录
;
/**
* 创建多级目录
*/
public class MkDirsDemo {
public static void main(String[] args) throws InterruptedException {
//当前目录下新建:a/b/c
File file = new File("a/b/c");
if (file.exists()){
System.out.println("文件已经存在");
}else {
file.mkdirs();
System.out.println("创建成功");
}
}
}
7.删除空目录
方法:delete()
;
/**
* 删除一个目录
*/
public class DeleteDirDemo {
public static void main(String[] args) {
//删除当前目录下的demo目录
File dir = new File("demo");
if(dir.exists()){
dir.delete();//删除目录的前提是这个目录必须是一个空目录
System.out.println("目录已删除!");
}else{
System.out.println("目录不存在!");
}
}
}
8. 获取当前目录下所有子项
方法:listFiles()
;
/**
* 获取一个目录中的所有子项
*/
public class ListFilesDemo {
public static void main(String[] args) {
//获取当前目录下的所有子项
File dir = new File(".");
/*
boolean isFile()
判断当前File表示的是一个文件
boolean isDirectory()
判断当前File表示的是一个目录
*/
if(dir.isDirectory()){
/*
File[] listFiles()
获取当前File表示的目录中的所有子项,每个子项都是一个File对象并最终以一个
数组形式返回.
*/
File[] subs = dir.listFiles();
System.out.println("当前目录中有"+subs.length+"个子项");
for(int i=0;i<subs.length;i++){
System.out.println(subs[i].getName());
}
}
}
}
9.文件过滤器
方法:File[] list(FileFilter filter)
;
该方法在获取该目录中子项的过程中利用参数给定的过滤器将满足条件的子项返回,其余的忽略
/**
* 有条件的获取一个目录中的子项.
* 重载的方法:
* File[] listFiles(FileFilter filter)
* 获取当前File表示的目录中满足给定过滤器要求的所有子项
*/
public class ListFilesDemo2 {
public static void main(String[] args) {
File dir = new File(".");
if(dir.isDirectory()){
/*
仅获取所有的.xml文件
*/
// FileFilter filter = new FileFilter(){
// /*
// 实现文件过滤器接口就必须重写accept方法.该方法是定义规律规则.
// 当给定的file符合过滤要求时,方法应当返回true
// */
// public boolean accept(File file) {
// String name = file.getName();
// boolean ends = name.endsWith(".xml");
// return ends;
// }
// };
// File[] subs = dir.listFiles(filter);
File[] subs = dir.listFiles(new FileFilter(){
public boolean accept(File file) {
String name = file.getName();
boolean ends = name.endsWith(".xml");
return ends;
}
});
System.out.println(subs.length);
for(int i=0;i<subs.length;i++){
System.out.println(subs[i].getName());
}
}
}
}
七、RandomAccessFile类
1.什么是RandomAccessFile类
java.io.RandomAccessFile能够对文件的内容进行读写操作
2.为什么需要RandomAccessFile类
File类只能创建,删除文件或获得文件信息,但是不能对文件内容进行操作,RandomAccessFile类可以对文件内容进行任意的读写
3.向文件写入和读取内容
RandomAccessFile类的两个构造:
1、 public RandomAccessFile(String name, String mode){
...}
2、 public RandomAccessFile(File file, String mode){
...}
构造 参数说明:
File file: 参数为一个文件
String name:指定一个文件的路径
String mode:指定读写模式 r:只读 rw:读写 注意:写之前一定要先读取文件,因为不读,直接写,程序是会报错的
方法:write()
;
/**
* java.io.RandomAccessFile
* RAF是专门用来读写文件的API,其基于指针对文件任意位置可进行读或写操作.
*/
public class RAFDemo1 {
public static void main(String[] args) throws IOException {
/*
对当前目录下的raf.dat文件写操作
RandomAccessFile常用的构造方法
RandomAccessFile(String path,String mode)
RandomAccessFile(File file,String mode)
第一个参数是要操作的文件,可以直接给路径或传入一个File对象.
第二个参数是操作模式,支持:"r"只读模式,"rw"读写模式
*/
RandomAccessFile raf = new RandomAccessFile("./raf.dat","rw");
// File file = new File("./raf.dat");
// RandomAccessFile raf = new RandomAccessFile(file,"r");
/*
向文件中写入一个字节,写入的是给定的int值对应的2进制的"低八位"
vvvvvvvv
00000000 00000000 00000001 00000001
*/
raf.write(1);
// 00000000 00000000 00000000 00000010
raf.write(2);
/*
文件中的数据:
11111111 00000010
*/
System.out.println("写出完毕!");
raf.close();
}
}
方法:read()能够从指定文件中读取一个字节
;
/**
* 从文件中读取字节
*/
public class RAFDemo2 {
public static void main(String[] args) throws IOException {
/*
文件中的数据:
11111111 00000010
*/
RandomAccessFile raf = new RandomAccessFile("raf.dat","r");
/*
int read()
从文件中读取一个字节,并将对应的8位2进制存入int值的低八位上,将该int值返回.
如果返回值为-1则表示文件读取到了末尾(EOF end of file)
*/
/*
00000000 00000000 00000000 00000001
*/
int d = raf.read();
System.out.println(d);
/*
00000000 00000000 00000000 00000010
*/
d = raf.read();
System.out.println(d);
d = raf.read();
System.out.println(d);//-1
raf.close();
}
}
4. 复制文件
配和write()和read():能够复制指定文件到新的位置
/**
* 文件复制
*/
public class CopyDemo {
public static void main(String[] args) throws IOException {
RandomAccessFile src = new RandomAccessFile("music.mp3","r");
RandomAccessFile desc = new RandomAccessFile("music_cp.mp3","rw");
/*
image.jpg的文件数据:
11010110 00101011 01011101 10100010 11000011 10101101 ....
image_cp.jpg
11010110 00101011 01011101 10100010 11000011 10101101 ....
*/
int d;//接收读取的字节
long start = System.currentTimeMillis();
while((d = src.read())!=-1) {
//先从原文件读取一个字节,判断是否读取到末尾
desc.write(d);//没读取到末尾就把读取的字节写入到复制的文件中
}
long end = System.currentTimeMillis();
System.out.println("复制完毕!,耗时:"+(end-start)+"ms");
src.close();
desc.close();
}
}
但是这种方式很不高效,为什么呢,下面我们来分析分析:
先来谈谈磁盘,我们知道文件都是存储在磁盘,没有磁盘会怎么样?
简单理解,没有磁盘,我们在操作数据的时候,如果突然的断电,那我们所写的数据也将不复存在,因为没有保存的地方,怎么把我们所写的数据存储起来呢,人们发现,磁和电很相似,电有两种状态:有/无,磁有南北极;于是人们把数据写入文件,及就是:电转磁的一个过程, 同理读取文件就是:磁转电的过程,这个过程是很慢的,这里就不详说了;而我们的cpu处理器的处理效率是很慢的,两个的对比可以这样形容:磁盘读取效率看作“跑的速度”,cpu处理效率就是“火箭飞的速度”;但是我们知道程序运行的效率是“短板效应”,操作磁盘会拉低cpu处理器速度,从而降低程序执行效率
我们来看上述代码操作:
每次读取和写入 单位字节
数据,如果文件很大,要进行多少次的对磁盘操作?这无疑是把磁盘操作效率低发挥到了极致啊
所以我们要改进,继续往下看
5. 复制文件优化
方法:块读int read(byte[] data)
;
一次性从文件中读取给定字节数组长度的字节量,并存入该字节数组; 如果返回值为-1,则表示EOF
方法:块写 void write(byte[]data
);
方法:块写 void write(byte[] data,int start,int end)
一次性将给定字节数组的所有数组写入文件写入
栗子:
public class CopyDemo2 {
public static void main(String[] args) throws Exception {
RandomAccessFile r1 = new RandomAccessFile("./D.txt","r");
RandomAccessFile r2 = new RandomAccessFile("./r2.txt","rw");
byte[]bytes = new byte[1024*10];//10kb
int len;//记录每次实际读取到的字节数
long l1 = System.currentTimeMillis();
while ((len = r1.read(bytes))!=-1){
r2.write(bytes);
}
long l2 = System.currentTimeMillis();
System.out.println((l2-l1));
r1.close();
r2.close();
}
}
我们分析下为什么这样可以提高程序运行效率:
我们通过提高每次读写的数据量,减少写读次数可以提高读写效率,也就是每次读取byte数组大小字节的数据量,将这个字节数组中的所有字节一次性写出提高运行效率!
注意上述程序这部分代码:
byte[]bytes = new byte[1024*10];//10kb
int len;//记录每次实际读取到的字节数
long l1 = System.currentTimeMillis();
while ((len = r1.read(bytes))!=-1){
r2.write(bytes);
}
我们byte数组为10kb大小,也就是我们每次可以读取10kb大小的数据量,但是我们的数据量没有10kb,我们写入:write(byte),还是写入的是10kb大小的数据量,这也就是我们有时候复制过去的文件比源文件大的原因。所以我们要用write的重载方法:
void write(byte[] data,int start,int end)
表示:从byte数组的start下标读取到end下标,这样就可以解决上述问题
6. 读写入文本(汉字)
我们在用写入/读取汉字的时候,如果不指定字符集,我们会看到我们写入/读取的汉字会有乱码问题,所以下面就教大家怎么去解决这问题
读方法:
byte[]data = new byte[(int) r.length()];
r.read(data)
写方法:
byte[]data = str.getBytes("utf-8");
r.write(data);
栗子:
/**
* 读写字符串
*/
public class WriteStringDemo {
public static void main(String[] args) throws Exception{
RandomAccessFile r = new RandomAccessFile("./D.txt","rw");
String line = "年轻人不讲武德,耗子尾汁";
byte[]data = line.getBytes("utf-8");
r.write(data);
r.write("我大意了,没有闪".getBytes("utf-8"));
System.out.println("写出完毕");
r.close();
/*
读文本数据:使用块读,将所有字节一次性读取
*/
byte[]bytes = new byte[(int)r.length()];
r.read(bytes);
String str = new String(bytes,"utf-8");
System.out.println(str);
r.close();
}
}
7.RAF的指针及使用
方法:seek(int pos)定义指针位置
;
public class RAFDemo3 {
public static void main(String[] args) throws Exception {
RandomAccessFile r = new RandomAccessFile("./D.txt","rw");
//写入int最大值
/*
01111111 11111111 11111111 11111111
文件中:01111111
*/
//默认建好RAF时候,指针默认在文件的最开始
Long pos = r.getFilePointer();
System.out.println("pos:"+pos);//0
int max = Integer.MAX_VALUE;
r.write(max>>>24);
System.out.println("pos:"+r.getFilePointer());
r.write(max>>>16);
r.write(max>>>8);
r.write(max);
System.out.println("pos:"+r.getFilePointer());
/*
RandomAccessFile提供了方便写出基本数据类型相关方法
writeInt(int sum);该方法等同于上面四句
*/
r.writeInt(max);
System.out.println("pos:"+r.getFilePointer());
r.writeLong(123);//连续写8字节,将lon值写入文件
r.writeDouble(123.123);
r.seek(0);
int d = r.readInt();
System.out.println(d);
r.seek(8);
System.out.println(r.readLong());
r.close();
}
}
8. 练习测试
1.用户注册练习
程序启动后顺序输入:用户名,密码,昵称,年龄
然后将其写入到指定文件中保存
要求:用户名,昵称,密码 为String类型 各占32字节,年龄为int 占4字节,总共占用100字节
代码:
/**
* 用户注册
* 用户信息包含四项:用户名 密码 昵称 年龄
*
*/
public class ReDemo {
public static void main(String[] args) throws IOException {
Scanner s = new Scanner(System.in);
System.out.println("输入用户名:");
String username = s.nextLine();
System.out.println("输入密码");
String password = s.nextLine();
System.out.println("昵称:");
String name = s.nextLine();
System.out.println("年龄:");
int age = s.nextInt();
RandomAccessFile r1 = new RandomAccessFile("./D.txt","rw");
r1.seek(r1.length());
//写入用户名
byte[]data = username.getBytes("utf-8");
data = Arrays.copyOf(data,32);
r1.write(data);
//密码
data = password.getBytes("utf-8");
data = Arrays.copyOf(data,32);
r1.write(data);
//昵称
data = name.getBytes("utf-8");
data = Arrays.copyOf(data,32);
r1.write(data);
//年龄
r1.writeInt(age);
r1.close();
}
}
2. 读取用户练习
显示上面指定文件中的所有用户信息
代码:
public class showUser {
public static void main(String[] args) throws Exception{
RandomAccessFile r = new RandomAccessFile("./D.txt","rw");
for (int i = 0;i<r.length()/100;i++){
byte[]bytes = new byte[32];
//读取用户名
r.read(bytes);
String username = new String(bytes,"utf-8").trim();
//读取密码
r.read(bytes);
String password = new String(bytes,"utf-8").trim();
//读取昵称
r.read(bytes);
String nickname = new String(bytes,"utf-8").trim();
//读取年龄
int age = r.readInt();
System.out.println(username+" "+password+" "+nickname+" "+age);
}
r.close();
}
}
3.修改用户练习
程序启动后要求输入用户名和密码,然后将上述给定文件下对应的记录修改,匹配上的用户名,修改为新的昵称
代码
/**
* 修改用户昵称
*/
public class UpdateDemo {
public static void main(String[] args) throws Exception{
Scanner s = new Scanner(System.in);
System.out.println("输入用户名:");
String username = s.nextLine();
System.out.println("输入新昵称:");
String nickname = s.nextLine();
RandomAccessFile r = new RandomAccessFile("./D.txt","rw");
boolean flag = false;//是否修改过记录
for (int i = 0;i<r.length()/100;i++){
r.seek(i*100);
//读取用户名
byte[]bytes = new byte[32];
r.read(bytes);
String name = new String(bytes,"utf-8").trim();
if (name.equals(username)){
//移动指针到昵称位置
r.seek(i*100+64);
bytes = nickname.getBytes("utf-8");
bytes = Arrays.copyOf(bytes,32);
r.write(bytes);
flag = true;
System.out.println("修改完毕");
break;
}
if (!flag){
System.out.println("查无此人");
}
r.close();
}
}
}
八、IO流
我的这篇博客:IO流,对IO流概念,分类,操作超详细详解,所以此篇博客我们浅谈下什么是IO流,及其分类和操作
1. 什么是io
IO:Input,Output java标准的输入和输出
java IO是以标准的操作对外界设备进行数据交换并将读写分为输入和输出
IO是顺序读写方式,只能顺序读取,并不能同时进行读和写操作
2.为什么需要IO
功能上与上面的RandomAccessFile相似,但是RAF是随机读写形式,而文件流是顺序读写形式,对于读写的灵活度不如RAF,但是基于流连接可以完成复杂的数据读写
3.文件流的使用
(所有的知识点我都在代码上备注了,望细看)
1.写操作
/**
* java IO 标准的输入与输出
* java IO将读写功能按照方向划分为输入与输出.
* 输入流:用于读取数据的流,是从外界到程序中的方向.
* 输出流:用于写出数据的流.
*
* java.io.InputStream和OutputStream是所有字节输入流与输出流的超类,规定了相关的
* 读写字节的方法.
*
* java将流归纳为两类:
* 节点流:又称为低级流,是真实连接程序与另一端的"管道",负责实际搬运数据的流.读写一定是建立
* 在节点流基础上进行的.
* 处理流:不能独立存在,必须连接在其他流上,目的是当数据"流经"当前流时对其进行某种加工处理
* 来简化我读写数据的操作.
*
* 文件流:java.io.FileOutputStream和FileInputStream
* 它们是一对低级流,用于读写文件数据的流.
*
*/
public class FOSDemo {
public static void main(String[] args) throws IOException {
/*
使用文件输出流向文件中写出字节
FileOutputStream常用的构造器:
FileOutputStream(String path)
FileOutputStream(File file)
以上两种构造器创建的文件输出流为覆盖写模式.即:如果指定的文件已经存在,会将该文件
原有的数据全部清除.然后再讲所有通过当前流写出的数据写入到文件中.
FileOutputStream(String path,boolean append)
FileOutputStream(File file,boolean append)
如果再传入一个boolean值参数,并且为true时,那么此时创建的文件输出流为追加模式.
文件中原数据保留,新写入的信息都被追加到文件末尾
*/
//文件流写文件要么将文件原数据全部清除后重新写,要么都保留并追加写.(只能顺序向后进行写操作)
FileOutputStream fos = new FileOutputStream("fos.txt",true);
//RAF是可以操作指针对任意位置进行写操作.这种方式也称为随机读写形式
// RandomAccessFile fos = new RandomAccessFile("fos.txt","rw");
// String str = "夜空中最亮的星";
// byte[] data = str.getBytes("UTF-8");
// fos.write(data);
// fos.write(",能否听清,那仰望的人心底的孤独和叹息.".getBytes("UTF-8"));
fos.write("我祈祷".getBytes("UTF-8"));
System.out.println("写出完毕!");
fos.close();
}
}
2.读操作
/**
* 文件输入流,用于读取文件数据
*/
public class FISDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("fos.txt");
byte[] data = new byte[1000];
int len = fis.read(data);
System.out.println("实际读取到了"+len+"个字节");
String str = new String(data,0,len,"UTF-8");
System.out.println(str);
System.out.println(str.length());
fis.close();
}
}
3.文件流复制文件
直接复制:
/**
* 使用文件流完成文件的复制操作
*/
public class CopyDemo {
public static void main(String[] args) throws IOException {
/*
1:创建文件输入流读取原文件
2:创建文件输出流用于写复制文件
3:循环从原文件读取字节并写入到复制文件中完成复制
4:关闭两个流
利用块读写形式复制.
*/
FileInputStream fis = new FileInputStream("music.mp3");
FileOutputStream fos = new FileOutputStream("music_cp.mp3");
int len;//记录每次实际读取到的字节数
byte[] data = new byte[1024*10];//10k缓冲区
while((len = fis.read(data)) != -1){
fos.write(data,0,len);
}
System.out.println("复制完毕!");
fis.close();
fos.close();
}
}
使用缓冲流
/**
* 使用缓冲流完成文件复制.
*
* 缓冲字节流:java.io.BufferedInputStream和BufferedOutputStream
* 它们是一对高级流,在流连接中的作用是加快读写字节的效率.
*/
public class CopyDemo2 {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("music.mp3");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("music_cp2.mp3");
BufferedOutputStream bos = new BufferedOutputStream(fos);
int d;//记录每次实际读取到的字节
long start = System.currentTimeMillis();
/*
缓冲流内部维护着一个8k的字节数组(默认是8k,可以在创建缓冲流时指定其他长度)
然后无论我们对缓冲流的读写操作是采取单字节读写还是块读写,都会被缓冲流转换为
块读写来保证读写效率
*/
while((d = bis.read()) != -1){
bos.write(d);
}
long end = System.currentTimeMillis();
System.out.println("复制完毕!耗时:"+(end-start)+"ms");
//关闭时只需要关闭最外层的高级流即可,它会自动关闭其连接的流
bis.close();
bos.close();
}
}
4.缓冲流的flush方法
/**
* 缓冲流写出数据时的缓冲区问题
*/
public class BOS_flushDemo {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("bos.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
String str = "让我再看你一遍,从南到北.";
byte[] data = str.getBytes("UTF-8");
bos.write(data);
/*
void flush方法是在java.io.OutputStream中定义的方法,所有字节输出流都支持
这个方法,但是实际上只有缓冲流真正实现了这个方法,用于将缓冲区中已经缓存的数据
一次性写出.其他流实现的目的只是用于传递该方法.
*/
bos.flush();
System.out.println("写出完毕!");
/*
缓冲流的close方法中会自动调用一次flush方法,确保关闭前缓冲区的数据会被写出
*/
bos.close();
}
}
4. IO 序列化
关于java IO序列化,看我这篇博客就可以了:IO序列化
最后谢谢大家观看!