JAVA 常用SE
String类
String是不变对象,即:内容一旦创建值不可改变,若改变字符串内容必定创建新对象
JVM对字符串的一个优化为常量池,会缓存所有用字面量形式创建的字符串对象并进行重用。
int length()
返回当前字符串得长度(字符个数)
String str = "我爱java!";
int len = str.length();
System.out.println(len);
char charAt(int index)
获取当前字符串中指定位置处的字符
// 0123456789012345
String str = "thinking in java";
char c = str.charAt(9);
System.out.println(c);
int indexOf(String str)
检索给定字符串在当前字符串中的位置
如果给定字符串在当前字符串中不存在,则返回值为-1
int indexOf(String str,int number)从指定位置处开始查找
int lastIndexOf(String str)查找最后一次出现给定字符串的位置
// 0123456789012345
String str = "thinking in java";
int index = str.indexOf("in");
System.out.println(index);
//可以从指定位置处开始查找
index = str.indexOf("in",3);
System.out.println(index);
//查找最后一次出现给定字符串的位置
index = str.lastIndexOf("in");
System.out.println(index);
boolean startsWith(String str)
boolean endsWith(String str)
判断当前字符串是否是以给定字符串开始或结尾
String str = "thinking in java";
boolean start = str.startsWith("thi");
System.out.println(start);//true
boolean end = str.endsWith("ava");
System.out.println(end);//true
String substring(int start,int end)
截取当前字符串中指定范围内的字符串。
两个参数分别是从哪里开始到哪里结束,需要注意的是javaAPI有一个特点,通常用两个数字表示范围时都是"含头不含尾"的
// 01234567890
String line = "www.tedu.cn";
//截取域名部分
String sub = line.substring(4,8);
System.out.println(sub);
sub = line.substring(4);
System.out.println(sub);
getHostName("www.baidu.com");
getHostName("http://doc.tedu.cn");
getHostName("www.canglaoshi.com.cn");
截取给定网址中的域名部分并输出
//找到第一个"."之后第一个字符的位置
int start = line.indexOf(".")+1;
//找到第二个"."的位置
int end = line.indexOf(".",start);
String sub = line.substring(start,end);
System.out.println(sub);
String toUpperCase()
String toLowerCase()
将当前字符串中的英文部分转换为全大写或全小写
String str = "我爱Java";
String upper = str.toUpperCase();
System.out.println(upper);
String lower = str.toLowerCase();
System.out.println(lower);
//验证码
String code = "1A72bc".toLowerCase();
String line = "1A72BC".toLowerCase();
System.out.println(code.equals(line));
String trim()
去除当前字符串两边的空白字符
String str = " hello ";
System.out.println(str);
String trim = str.trim();
System.out.println(trim);
static String valueOf(XXX xxx)
String有一组重载的静态方法valueOf,作用是将给定内容转换为字符串
int a = 123;
String s1 = String.valueOf(a);//"123"
System.out.println(s1);
double d = 123.123;
String s2 = String.valueOf(d);
System.out.println(s2);
String s3 = a+"";
System.out.println(s3);
java.lang.StringBuilder
String的优化是针对重用性的,频繁修改字符串会创建大量的垃圾对象,导致内存消耗大,运行性能差。
StringBuilder是专门解决字符串修改问题的类,内部维护一个可变的char数组,并且提供了修改字符串内容的相关方法:增,删,改,插。
append():追加指定内容
replace():修改字符串内容
delete():删除部分内容
String str = "好好学习java";
//默认表示一个空字符串 char[]
// StringBuilder builder= new StringBuilder();
//基于一个给定的字符串创建
StringBuffer builder= new StringBuffer(str);
/*
* 好好学习java
* 好好学习java,为了找个好工作!
* append():追加指定内容
*/
builder.append(",为了找个好工作! ");
System.out.println(builder);
/*
* 好好学习java,为了找个好工作!
* 好好学习java,就是为了改变世界!
* replace():修改字符串内容
*/
builder.replace(9, 16, "就是为了改变世界");
System.out.println(builder);
/*
* 好好学习java,就是为了改变世界!
* ,就是为了改变世界!
* delete():删除部分内容
*/
builder.delete(0, 8);
System.out.println(builder);
/*
* ,就是为了改变世界!
* 活着,就是为了改变世界!
*/
builder.insert(0, "活着");
System.out.println(builder);
object类
/**
* 使用当前类测试Object常被重写的方法
* @author Administrator
*
*/
public class Point {
private int x;
private int y;
public Point(int x, int y) {
super();
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
/**
* toString方法返回的字符串没有严格的格式
* 要求,可结合实际情况而定,但是通常该字符
* 串应当包含当前对象的属性信息。
*/
public String toString() {
return "("+x+","+y+")";
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Point other = (Point) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
// public boolean equals(Object obj) {
// if(obj==null) {
// return false;
// }
// if(obj == this) {
// return true;
// }
// if(obj instanceof Point) {
// Point p = (Point)obj;
// return this.x==p.x&&this.y==p.y;
// }
// return false;
// }
测试Object相关方法
Point p = new Point(1,2);
/*
* System.out.println(Object o)
* 该方法会将给定对象的toString方法
* 返回的字符串输出到控制台。
*
* Object的toString方法是常见的会被
* 子类重写的方法,该方法原始定义返回
* 的字符串是对象的"句柄",即:地址信息
* 但是实际价值小。
* 该方法我们将来很少会直接调用,都是
* 在调用某个API时被自动调用,好比上面
* 提到的输出控制台。
*/
System.out.println(p);
Point p2 = new Point(1,2);
System.out.println(p2);
/*
* 对于引用类型而言:
* ==的比较是比较两个变量的值,即:地址
* 是否相同,因此==的意义是比较两个变量
* 指向的是否为"同一个对象"
*
* equals方法是Object提供的方法,设计
* 意义是比较两个变量指向的对象"内容是否
* 相同"。但是该方法需要子类重写,否则
* 比较就是"==".
*/
System.out.println(p==p2);//false
System.out.println(p.equals(p2));//true
String s1 = "123abc";
String s2 = new String("123abc");
System.out.println(s1==s2);//false
System.out.println(s1.equals(s2));
integer包装类
包装类是用来解决基本类型不能直接参与面向对象开发的问题。使得基本类型可以以"对象"的形式存在
基本类型转换为包装类
Integer i1 = new Integer(1);
Integer i2 = new Integer(1);
java推荐用valueOf转换
Integer i1 = Integer.valueOf(123);
Integer i2 = Integer.valueOf(123);
包装类转换为基本类型
int i = d1.intValue();
double d = d1.doubleValue();
byte b = d1.byteValue();
//基本类型转换为包装类
//Integer i1 = new Integer(1);
//Integer i2 = new Integer(1);
//java推荐用valueOf转换
Integer i1 = Integer.valueOf(123);
Integer i2 = Integer.valueOf(123);
System.out.println(i1==i2);
System.out.println(i1.equals(i2));
Double d1 = Double.valueOf(128.0);
Double d2 = Double.valueOf(128.0);
System.out.println(d1==d2);
System.out.println(d1.equals(d2));
//包装类转换为基本类型
double d = d1.doubleValue();
System.out.println(d);
int i = d1.intValue();
System.out.println(i);
byte b = d1.byteValue();
System.out.println(b);
包装类常用功能
Integer.MAX_VALUE;
Integer.MIN_VALUE;
获取基本类型的最大值与最小值
int imax = Integer.MAX_VALUE;
System.out.println(imax);
int imin = Integer.MIN_VALUE;
System.out.println(imin);
long lmax = Long.MAX_VALUE;
System.out.println(lmax);
包装类可以将字符串解析为基本类型,前提是该字符串正确描述了基本类型可以保存的值
String str = "123";
int i = Integer.parseInt(str);
System.out.println(i);//123
double d = Double.parseDouble(str);
System.out.println(d);//123.0
JDK1.5版本推出时,推出了一个新特性:
自动拆装箱,可以让编译器在编译期间自动将基本类型与引用类型之间互转。
int a = 1;
/*
* 触发了编译器的自动装箱特性
* 下面代码会被编译器改为:
* Integer i = Integer.valueOf(i);
*/
Integer i = a;
//触发自动拆箱特性
//a = i.intValue();
a = i;
file类
java.io.File
File用于表示硬盘上的一个文件或目录的(实际上是一个抽象路径)
使用File可以:
1:访问其表示的文件或目录的属性(名字,大小等)
2:操作文件或目录(创建,删除)
3:访问一个目录中的所有子项
但是不能访问文件数据。
创建File时要指定路径,这里使用路径通常用相对路径(可以做到跨平台)
"./"指的是当前目录,具体是哪里要视程序运行的环境而定。在运行程序时,当前目录是这个程序所在的项目
getName() 获取文件名
length() 获取文件大小(单位是字节)
boolean canRead() 是否可读,返回布尔值
boolean canWrite() 是否可写,返回布尔值
File file = new File("./test.txt");
//获取文件名
System.out.println(file.getName());
//获取文件大小(单位是字节)
System.out.println(file.length());
//是否可读可写
boolean cr = file.canRead();
boolean cw = file.canWrite();
System.out.println("可读:"+cr);
System.out.println("可写:"+cw);
使用File创建一个文件
createNewFile() 创建文件
boolean exists() 判断当前File表示的文件或目录是否已经存在了
/*
* 在当前目录下新建文件demo.txt
*/
File file = new File("./demo.txt");
/*
* boolean exists()
* 判断当前File表示的文件或目录是否已经
* 存在了
*/
if(!file.exists()) {
file.createNewFile();
System.out.println("文件已创建!");
}else {
System.out.println("文件已存在!");
}
删除一个文件
delete() 删除一个文件
/*
* 将当前目录下的文件:demo.txt文件删除
*/
File file = new File("./demo.txt");
if(file.exists()) {
file.delete();
System.out.println("文件已删除!");
}else {
System.out.println("文件不存在!");
}
创建一个目录
mkdir() 创建目录
/*
* 在当前目录下新建目录:demo
*/
File dir = new File("./demo");
if(!dir.exists()) {
dir.mkdir();
System.out.println("目录已创建!");
}else {
System.out.println("目录已存在!");
}
创建一个多级目录
mkdirs() 创建多级目录
` /*
* 在当前目录下新建:a/b/c/d/e/f
*/
File dir = new File("./a/b/c/d/e/f");
if(!dir.exists()) {
//自动将不存在的父目录一同创建
dir.mkdirs();
System.out.println("目录已创建!");
}else {
System.out.println("目录已存在!");
}
删除目录
delete() 删除目录,与删除文件方式一样
/*
* 将当前目录下的demo目录删除
*/
File dir = new File("./demo");
if(dir.exists()) {
/*
* 删除目录的前提是该目录是空目录
*/
dir.delete();
System.out.println("目录已删除!");
}else {
System.out.println("目录不存在!");
}
获取一个目录中的所有子项
boolean isFile() 判断是否为文件
boolean isDirectory() 判断是否为目录
/*
* 获取当前目录中的所有子项
*/
File dir = new File(".");
/*
* boolean isFile()
* 判断是否为文件
*
* boolean isDirectory()
* 判断是否为目录
*/
if(dir.isDirectory()) {
File[] subs = dir.listFiles();
System.out.println(subs.length);
for(int i=0;i<subs.length;i++) {
System.out.println(subs[i].getName());
}
}
listFiles
重载的listFiles方法允许我们指定一个文件过滤器,然后在获取一个目录子项的过程中根据该过滤器的规则最终获取符合要求的所有子项
/*
* 获取一个目录中所有名字以"."开始的
* 子项
*/
File dir = new File(".");
if(dir.isDirectory()) {
FileFilter filter = new FileFilter() {
/**
* 用于定义过滤规则,如果方法参数指定的
* file表示的文件或目录符合要求,当前方法
* 应当返回为true
*/
public boolean accept(File file) {
String name = file.getName();
System.out.println("正在过滤:"+name);
return name.startsWith(".");
}
};
File[] subs = dir.listFiles(filter);
System.out.println(subs.length);
for(int i=0;i<subs.length;i++) {
System.out.println(subs[i].getName());
}
}
raf(java.io.RandomAccessFile)
RAF是专门用于读写文件数据的API,其基于指针对文件进行随机访问。是非常灵活的读写文件的API
write(int a)给定的int值对应的2进制的"低八位"
close() 关闭
/*
* 对当前目录下的raf.dat文件进行操作
* 第一个参数为要操作的文件,可以为:
* 1:直接将路径的字符串指定
* 2:传入File对象
*
* 第二个参数为操作模式,有两个常用:
* "r":只读模式
* "rw":读写模式
*/
RandomAccessFile raf
= new RandomAccessFile(
"./raf.dat","rw");
/*
* void write(int d)
* 向文件中写入一个字节,写入的是
* 给定的int值对应的2进制的"低八位"
* vvvvvvvv
* 00000000 00000000 00000000 00000001
* 00000000 00000000 00000001 00000001
*/
raf.write(69);
System.out.println("写出完毕!");
//关闭
raf.close();
}
read() 如果返回的int值为-1,则表示已经读取到文件末尾了
从文件中读取字节
RandomAccessFile raf = new RandomAccessFile("./raf.dat","r");
/*
* 从文件中读取1个字节,返回的int值
* 实际上只有低八位有数据。
* 如果返回的int值为-1,则表示已经读取
* 到文件末尾了
* 10110101
* 00000000 00000000 00000000 10110101
*/
int d = raf.read();
System.out.println(d);
d = raf.read();//-1
System.out.println(d);
//关闭
raf.close();
文件复制
RandomAccessFile src = new RandomAccessFile("d:/czh.zip","r");
RandomAccessFile desc = new RandomAccessFile("d:/czh2.zip","rw");
int d = -1;//用于记录每次读取到的字节
while( (d = src.read()) != -1 ) {
desc.write(d);
}
System.out.println("复制完毕!");
src.close();
desc.close();
批量读取文件
使用byte数组来存储读取到的数据
RandomAccessFile raf = new RandomAccessFile("D:/ftp.zip", "r");
//盘子
byte[] buf = new byte[1024*8];
int n;//每次读取个数
while((n = raf.read(buf))!=-1) {
System.out.println(n);
}
System.out.println(n);
raf.close();
批量(缓存)读写复制文件
/*
* 批量(缓存)读写复制文件
*/
RandomAccessFile in= new RandomAccessFile("D:/czh.zip", "r");
RandomAccessFile out= new RandomAccessFile("D:/czh2.zip", "rw");
int n;
byte[] buf = new byte[1024*8];
while((n=in.read(buf))!=-1) {
out.write(buf, 0, n);
}
in.close();
out.close();
System.out.println("Done!");
将一个int数据写到文件中
getFilePointer() 获取当前文件读写位置
writeInt(int a)将整数a拆分为4个byte写到文件中
raf.readInt() 从文件中连续读取4个
seek() 检索,这里是移动文件指针到指定,如果是0 就是移动为文件开头
RandomAccessFile raf = new RandomAccessFile( "./demo.dat", "rw");
//getFilePointer() 获取当前文件读写位置
long p = raf.getFilePointer();
//文件刚刚打开时候,文件指针在0位置
System.out.println(p); //0
int n = 0x6a8d9f32;
System.out.println(n);
/*
* 将整数n拆分为4个byte写到文件中
*/
raf.writeInt(n);
//int写到文件里,占用4个字节,
//文件指针移动到 4
p = raf.getFilePointer();
System.out.println(p);//4
//seek:检索,这里是移动文件指针到指定
//的位置, 如果是0 就是移动为文件开头
raf.seek(0);
p = raf.getFilePointer();
System.out.println(p);//0
//将文件指定移动到0位置,就可以读取
//检查写入的int数据。
/*
* raf.readInt() 从文件中连续读取4个
* byte数据拼接为一个int数据
* 如果 返回的k和前面n一致,则
* 说明int数据读写成功
*/
int k = raf.readInt();
System.out.println(k);
//文件指针在哪里?
p = raf.getFilePointer();
System.out.println(p);
raf.close();
读写Long类型数据
writeLong() 将long类型数据拆分为8个Byte写到文件中,文件指针移动8次
raf.readLong() 从文件中连续读取8个
RandomAccessFile raf = new RandomAccessFile("demo02.dat", "rw");
long l = Long.MAX_VALUE;
//writeLong(l) 将long类型数据拆分为
//8个Byte写到文件中,文件指针移动8次
raf.writeLong(l);
raf.seek(0);
//readLong()从文件中连续读取8个字节
//拼接为一个long类型返回,文件指针移动8次
long n = raf.readLong();
System.out.println(n);
raf.close();
写入字符(UTF-16BE)
/*
* 写入字符(UTF-16BE)
*/
RandomAccessFile raf = new RandomAccessFile("demo8.txt", "rw");
raf.writeChar('A');
raf.writeChar('B');
raf.writeChar('中');
raf.close();
写一个UTF-8的文件
/*
* 写一个UTF-8的文件
*/
RandomAccessFile raf = new RandomAccessFile("demo9.txt", "rw");
String str = "ABCγ您吃了吗?";
//.getBytes("UTF-8") 将字符串中的每个字
//符按照UTF-8编码规则,编成 字节数组 。
//英文1字节,中文3字节
// γ 编码为2字节
byte[] utf8 = str.getBytes("UTF-8");
//将UTF-8编码以后的字节写到文件中
raf.write(utf8);
raf.close();
I/O流
java IO 标准的输入与输出操作
java IO 是将读和写操作按照方向进行划分
输入流InputStream:是从外界到程序的方向,是输入的方向,用来读取数据
输出流OutputSTream:是写出数据的操作
流可以比喻为连接程序与数据源的管道,而数据相当于管道中的水,顺序的输入或输出。因此使用IO读写数据是顺序向后进行的,不可回退。
java将流划分为两类:
-
节点流:又称为低级流,是真实连接程序与数据源之间的管道,读写数据一定是建立在节点流的基础上进行的。
-
处理流:又称为高级流,不能独立存在,必须连接在其它流上,这样在数据读写的过程中当流经当前
流时可以对其进行某些加工处理。简化我们读写数据的操作。
-
文件流:文件流是一对低级流,用于读写文件的流,在功能上与RandomAccessFile一致,但是文件流是基于java标准IO的顺序读写形式,而RAF是基于指针的随机读写形式。
FileInputStream使用文件输入流读取文件数据
int read(byte[] data) 一次性读取给定字节数组总长度的字节量并存入该数组,返回值为实际读取到的字节量,若返回为值-1表示文件末尾
FileInputStream fis = new FileInputStream("fos.txt");
byte[] data = new byte[1024*8];
/*
* int read(byte[] data)
* 一次性读取给定字节数组总长度的字节量
* 并存入该数组,返回值为实际读取到的字
* 节量,若返回为值-1表示文件末尾
*/
int len = fis.read(data);
String str = new String(data,0,len,"UTF-8");
System.out.println(str);
fis.close();
FOS(FileOutputStream)的构造方法:
对于一个参数的构造方法(String或File)创建的文件输出流是覆盖写模式,即:操作的文件若存在,文件流会将该文件数据抹除然后再开始将所有通过当前流写出的数据写 入该文件。
构造方法支持重载,可以再传入一个boolean 值类型的参数,如果该值为true,则是追加模式,文件原有数据保留,写出的数据都会被追加到文件中。
/*
* FOS的构造方法:
* 对于一个参数的构造方法(String或File)
* 创建的文件输出流是覆盖写模式,即:操作
* 的文件若存在,文件流会将该文件数据抹除
* 然后再开始将所有通过当前流写出的数据写
* 入该文件。
*
* 构造方法支持重载,可以再传入一个boolean
* 值类型的参数,如果该值为true,则是追加
* 模式,文件原有数据保留,写出的数据都会
* 被追加到文件中。
*/
FileOutputStream fos
= new FileOutputStream(
"./fos.txt",true);
// String str = "来~左边跟我一起画个龙~";
String str = "右边画一道彩虹.";
byte[] data = str.getBytes("UTF-8");
fos.write(data);
System.out.println("写出完毕!");
fos.close();
使用文件流完成文件的复制操作
/*
* 1:创建文件输入流读取原文件
* 2:创建文件输出流写复制文件
* 3:循环利用文件输入流读取原文件字节并
* 利用文件输出流写入复制文件
* 4:复制完毕后将两个流关闭
*
* 使用块读写方式复制
*/
FileInputStream fis= new FileInputStream("image.png");
FileOutputStream fos= new FileOutputStream("image_cp.png");
int len = -1;
byte[] data = new byte[1024*8];
while((len = fis.read(data))!=-1) {
fos.write(data,0,len);
}
System.out.println("复制完毕!");
fis.close();
fos.close();
使用当前类实例测试对象流的对象读写操作
/**
* 使用当前类实例测试对象流的对象读写操作
*
* 所有要被对象流序列化的类必须实现接口:
* java.io.Serializable
* 否则序列化过程中会抛出异常。
*
* @author Administrator
*
*/
public class Person implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private int age;
private String gender;
private transient String[] otherInfo;
public Person(String name, int age, String gender, String[] otherInfo) {
super();
this.name = name;
this.age = age;
this.gender = gender;
this.otherInfo = otherInfo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String[] getOtherInfo() {
return otherInfo;
}
public void setOtherInfo(String[] otherInfo) {
this.otherInfo = otherInfo;
}
public String toString() {
return name+","+age+","+gender+","+
Arrays.toString(otherInfo);
}
}
对象流
java.io.ObjectOutputStream
java.io.ObjectInputStream
对象流是一对高级流,作用是方便我们读写java对象。对象与字节的转换对象流在读写过程中自动完成。
String name = "hhrz";
int age = 18;
String gender = "男";
String[] otherInfo = {"java","mysql","html"};
Person p = new Person(name,age,gender,otherInfo);
System.out.println(p);
//用于将字节写入文件
FileOutputStream fos= new FileOutputStream("person.obj");
/*
* 用于将对象序列化
* 序列化:将对象按照其结构转换为一组字节
* 的过程
*/
ObjectOutputStream oos= new ObjectOutputStream(fos);
/*
* 对象输出流提供的writeObject方法可以将
* 给定的对象序列化后写出
* 这里就通过其连接的文件流最终将字节写入
* 到文件中了。
*/
oos.writeObject(p);
System.out.println("写出完毕!");
oos.close();
对象输入流,用于进行对象反序列化
readObject() 用于进行对象的反序列化操作。
FileInputStream fis= new FileInputStream("person.obj");
ObjectInputStream ois= new ObjectInputStream(fis);
/*
* 对象输入流提供的readObject方法
* 用于进行对象的反序列化操作。
* 需要注意,读取的数据源对应的字节
* 应当是利用对象输出流序列化的一个
* 对象所对应的一组字节,否则这里会
* 抛出异常。
*/
Person p = (Person)ois.readObject();
System.out.println(p);
ois.close();
缓冲流
java.io.BufferedOutputStream
java.io.BufferedInputStream
缓冲流是一对高级流,功能是提高读写效率。
在流连接中使用它们时,无论我们实际读写是采取单字节读写还是块读写,经过它们时都会被统一转换为块读写来保证读写效率。
FileInputStream fis = new FileInputStream("movie.mp4");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("movie_cp.mp4");
BufferedOutputStream bos = new BufferedOutputStream(fos);
int d = -1;
while((d = bis.read())!=-1) {
bos.write(d);
}
System.out.println("复制完毕!");
bis.close();
bos.close();
缓冲输出流写出数据的缓冲区问题(有可能还在缓冲区,在缓冲区没满时无法写出,需要调用flush()方法强制写出缓冲区数据)
flush()将缓冲流中已经缓存的数据强制写出频繁调用flush会降低写出效率,但是可以做到写出数据的即时性。
FileOutputStream fos = new FileOutputStream("bos.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
String str = "缓冲区问题";
byte[] data = str.getBytes("GBK");
bos.write(data);
/*
* void flush()
* 将缓冲流中已经缓存的数据强制写出
* 频繁调用flush会降低写出效率,但是
* 可以做到写出数据的即时性。
*/
bos.flush();
System.out.println("写出完毕!");
bos.close();
字符流
java将流按照读写单位划分为字节流与字符流
字节流的超类:java.io.InputStream,java.io.OutputStream
字符流的超类:java.io.Reader,java.io.Writer
由于字符流的最小读写单位为字符,因此字符流只适合读写文本数据!实际上底层本质还是读写字节,只不过字节与字符的转换由字符流完成。
转换流:
java.io.InputStreamReader
java.io.OutputStreamWriter
它们是字符流的一对常用实现类,在流连接中是非常重要的一环(但是将来通常不直接操作这两个流)
//向文本文件中写入文本数据
FileOutputStream fos
= new FileOutputStream("osw.txt");
/*
* 创建OSW时通常会指定第二个参数:字符集
* 这样写出的字符都会按照该字符集转换为
* 字节后写出。否则是按系统默认字符集写
*/
OutputStreamWriter osw
= new OutputStreamWriter(fos,"UTF-8");
/*
* 字符流支持直接传入字符串的write方法:
* void write(String str)
*/
osw.write("我可以接受你的所有,所有小脾气.");
osw.write("我可以带你去吃很多,很多好东西.");
System.out.println("写出完毕!");
osw.close();
缓冲字符流
java.io.BufferedWriter
java.io.BufferedReader
缓冲个字符流是一对高级流,可以保证读写文本数据的效率。并且可以按行读写文本数据
java.io.PrintWriter具有自动行刷新功能的缓冲字符输出流(内部总是连接BufferedWriter作为其缓冲功能)
/*
* PrintWriter提供了对文件写操作的构造方法
* PrintWriter(String path)
* PrintWriter(File file)
* 并且还提供了对应的重载,可以再传入一个
* String类型的参数,该参数为字符集。
*
*/
PrintWriter pw
= new PrintWriter("pw.txt","GBK");
pw.println("hhrz");
pw.println("java~");
System.out.println("写出完毕!");
pw.close();
缓冲字符输入流
java.io.BufferedReader
/*
* 将当前程序源代码读取出来并输出到控制台
*/
FileInputStream fis
= new FileInputStream(
"./src/io/BRDemo.java");
InputStreamReader isr
= new InputStreamReader(fis);
BufferedReader br
= new BufferedReader(isr);
String line = null;
/*
* BufferedReader提供的方法:
* String readLine()
* 返回一行字符串(不包含最后的换行符)。
* 如果返回值为null则表示流读取到了末尾。
* 如果某一行内容只有换行符,则返回值为
* 空字符串。
*/
while((line = br.readLine())!=null) {
System.out.println(line);
}
br.close();
在流连接中使用PW
FileOutputStream fos = new FileOutputStream("pw.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos,"GBK");
BufferedWriter bw = new BufferedWriter(osw);
PrintWriter pw = new PrintWriter(bw);
pw.println("你好!");
pw.println("再见!");
System.out.println("写出完毕!");
pw.close();
异常
异常处理机制之try-catch
语法:
try{
代码片段
}catch(XXXException e){
try中出现XXXException后的处理代码
}
System.out.println("程序开始了");
try {
String str = "a";
System.out.println(str.length());
System.out.println(str.charAt(0));
System.out.println(Integer.parseInt(str));
/*
* try语句块中出错代码以下的内容都
* 不会执行
*/
System.out.println("!!!!!");
}catch(NullPointerException e){
System.out.println("出现了空指针!");
/*
* catch可以定义多个,针对try中不同异常有不同
* 处理手段时,我们要分别捕获这些异常
*/
}catch(StringIndexOutOfBoundsException e) {
System.out.println("字符串下标越界了!");
/*
* 最后一个catch捕获Exception可以避免因为
* 一个未处理的异常导致程序中断。
*/
}catch(Exception e) {
System.out.println("反正就是出了个错!");
}
System.out.println("程序结束了");
finally块
finally是异常处理机制的最后一块,只要当程序执行到try当中,无论try当中的代码是否出现了异常,finally都必定执行。
通常我们将无关乎报错,但是都必须执行的代码放在这里确保运行。比如IO操作后的关闭流。
System.out.println("程序开始了");
try {
String str = "";
System.out.println(str.length());
return;
} catch (Exception e) {
System.out.println("出错了");
} finally {
System.out.println("finally中的代码执行了!");
}
System.out.println("程序结束了");
在IO操作中使用异常处理机制
FileOutputStream fos = null;
try {
fos = new FileOutputStream("fos.dat");
fos.write(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(fos!=null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
JDK7之后推出了一个特性:autocloseable
自动关闭特性。
try(
/*
* 只有实现了AutoCloseable接口的类才可以
* 在这里初始化,并且编译器会将它们在
* finally中调用close方法关闭。
*/
FileOutputStream fos
= new FileOutputStream("fos.dat");
){
fos.write(1);
} catch (Exception e) {
e.printStackTrace();
}
多线程thread
多线程可以让多个代码片段"同时"运行,事实上是并发运行的,多个代码片段是走走停停的。
创建线程有两种方式
方式一:定义一个类继承Thread并重写run方法。
第一种创建线程的方式优点为:简单,直接,适合匿名内部类形式创建
缺点:
- 1:继承Thread,由于java是单继承,这会导致无法再继承其他类复用方法,实际开发很不便。
- 2:线程任务定义在线程中,导致线程重用性变差
start() 用于启动线程
Thread t1 = new MyThread1();
Thread t2 = new MyThread2();
/*
* start方法用于启动线程,使得线程纳入
* 线程调度中得以并发运行。当线程启动后
* 第一次分配到时间片后会自动调用run方法
* 开始执行任务。因此我们不要主动调用该
* 线程的run方法。
*/
t1.start();
t2.start();
class MyThread1 extends Thread{
public void run() {
for(int i=0;i<1000;i++) {
System.out.println("你好!");
}
}
}
class MyThread2 extends Thread{
public void run() {
for(int i=0;i<1000;i++) {
System.out.println("hello!");
}
}
第二种创建线程的方式:
实现Runnable接口单独定义线程任务
//实例化线程任务
Runnable r1 = new MyRunnable1();
Runnable r2 = new MyRunnable2();
//创建线程
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
class MyRunnable1 implements Runnable{
public void run() {
for(int i=0;i<1000;i++) {
System.out.println("你好!");
}
}
}
class MyRunnable2 implements Runnable{
public void run() {
for(int i=0;i<1000;i++) {
System.out.println("hello!");
}
}
}
使用匿名内部类来完成两种线程的创建方式
//方式一
Thread t1 = new Thread() {
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("你好?");
}
}
};
//方式二
Runnable r2 = new Runnable() {
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("hello!");
}
}
};
Thread t2 = new Thread(r2);
t1.start();
t2.start();
获取线程相关信息的方法
Thread main = Thread.currentThread();
//获取线程名字
String name = main.getName();
System.out.println(name);
//获取线程的唯一标识
long id = main.getId();
System.out.println(id);
//获取线程优先级
int priority = main.getPriority();
System.out.println(priority);
//线程是否活着
boolean isAlive = main.isAlive();
System.out.println(isAlive);
//是否为守护线程
boolean isDaemon = main.isDaemon();
System.out.println(isDaemon);
//是否被中断了
boolean interrupted = main.isInterrupted();
System.out.println(interrupted);
java中所有的代码都是线程运行的。main方法是虚拟机启动起来后创建的第一条线程来运行的。
该线程的名字称为"main"也称为主线程。
线程提供了一个静态方法:
static Thread currentThread()
该方法可以获取运行这个方法的线程
//获取运行main方法的线程(主线程)
Thread main = Thread.currentThread();
System.out.println("运行main方法的线程是:"+main);
dosome();
Thread t = new Thread() {
public void run() {
Thread t = Thread.currentThread();
System.out.println("自定义线程:"+t);
dosome();
}
};
t.start();
public static void dosome() {
//获取运行dosome方法的线程
Thread t = Thread.currentThread();
System.out.println(
"运行dosome方法的线程是:"+t);
}
线程优先级
线程有10个优先级,分别用整数1-10表示
其中1表示最低优先级,10是最高优先级,5是默认值。
理论上优先级越高的线程获取时间片的次数越多。
Thread min = new Thread() {
public void run() {
for(int i=0;i<10000;i++) {
System.out.println("min");
}
}
};
Thread norm = new Thread() {
public void run() {
for(int i=0;i<10000;i++) {
System.out.println("nor");
}
}
};
Thread max = new Thread() {
public void run() {
for(int i=0;i<10000;i++) {
System.out.println("max");
}
}
};
max.setPriority(Thread.MAX_PRIORITY);
min.setPriority(Thread.MIN_PRIORITY);
min.start();
norm.start();
max.start();
static void sleep(long ms)
使运行该方法的线程阻塞指定毫秒,超时后线程会自动回到RUNNABLE状态再次等待获取时间片并发运行
System.out.println("程序开始了");
/*
* 完成倒计时程序
* 在控制台输入一个数字,然后每秒递减
* 到0的时候输出:时间到
*
*/
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("程序结束了");
多线程安全问题
当多个线程并发操作同一临界资源时,由于线程切换的时机不确定,导致执行操作的顺序混乱,未按代码设计顺序进行而出现各种问题。
临界资源:只能被单线程操作的资源
Table table = new Table();
Thread t1 = new Thread() {
public void run() {
while(true) {
int bean = table.getBean();
Thread.yield();
System.out.println(
getName()+":"+bean);
}
}
};
Thread t2 = new Thread() {
public void run() {
while(true) {
int bean = table.getBean();
Thread.yield();
System.out.println(getName()+":"+bean);
}
}
};
t1.start();
t2.start();
class Table{
private int beans = 20;
/**
* 当一个方法使用synchronized关键字修饰后
* 该方法称为"同步方法",即:不允许多个线程
* 同时在方法内部执行。
* 将多个线程异步操作改为同步操作就可以解决
* 多线程并发的安全问题了。
*/
public synchronized int getBean() {
if(beans==0) {
throw new RuntimeException("没有豆子了!");
}
//主动放弃本次时间,模拟线程切换
Thread.yield();
return beans--;
}
}
同步块
语法:
synchronized(同步监视器对象){
需要同步执行的代码片段
}
同步块可以更准确的锁定需要多个线程同步执行的代码片段。有效的缩小同步范围可以在保证并发安全的前提下尽可能的提高并发效率。
Shop shop = new Shop();
Thread t1 = new Thread() {
public void run() {
shop.buy();
}
};
Thread t2 = new Thread() {
public void run() {
shop.buy();
}
};
t1.start();
t2.start();
class Shop{
/*
* 在方法上使用synchronized,那么同步监视器
* 对象就是方法所属对象,即:this
*/
// public synchronized void buy() {
public void buy() {
try {
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在挑衣服...");
Thread.sleep(5000);
/*
* 同步块要求指定同步监视器对象,即:
* 上锁的对象,该对象可以是java中任何引用
* 类型的实例,只要保证多个需要同步执行该
* 代码块的线程看到的这个对象是同一个即可。
*
*/
synchronized (this) {
System.out.println(t.getName()+":正在试衣服...");
Thread.sleep(5000);
}
System.out.println(t.getName()+":结账离开");
} catch (Exception e) {
e.printStackTrace();
}
}
}
静态方法全局就一份,所以在静态方法上使用synchronized修饰,那么该方法一定具有同步效果。
Thread t1 = new Thread() {
public void run() {
Boo.dosome();
}
};
Thread t2 = new Thread() {
public void run() {
Boo.dosome();
}
};
t1.start();
t2.start();
class Boo{
/*
* 静态方法指定的同步监视器对象为当前类的
* 类对象。(Class的实例)
* JVM中每个被加载的类都有且只有一个Class
* 的实例与之对应。这就是类对象。反射知识
* 点会介绍。
*/
// public synchronized static void dosome() {
public static void dosome() {
synchronized (Boo.class) {
try {
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在执行dosome方法...");
Thread.sleep(5000);
System.out.println(t.getName()+":执行dosome方法完毕!");
} catch (Exception e) {
}
}
}
}
互斥锁
当时用synchronized锁定多个代码片段,但是指定的同步监视器对象是同一个时,这些代码片段间就是互斥的。
public static void main(String[] args) {
Foo foo = new Foo();
Thread t1 = new Thread() {
public void run() {
foo.methodA();
}
};
Thread t2 = new Thread() {
public void run() {
foo.methodB();
}
};
t1.start();
t2.start();
}
class Foo{
public synchronized void methodA() {
try {
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在执行A方法");
Thread.sleep(5000);
System.out.println(t.getName()+":执行A方法完毕");
} catch (Exception e) {
}
}
public synchronized void methodB() {
try {
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在执行B方法");
Thread.sleep(5000);
System.out.println(t.getName()+":执行B方法完毕");
} catch (Exception e) {
}
}
}
守护线程
守护线程也称为后台线程。默认创建的线程都是普通线程,守护线程是通过调用线程的setDaemon方法转变为的。
守护线程使用上与普通线程没有区别,但是在结束时机上有一点不同:进程的退出。
当一个进程中所有的普通线程都结束时,进程就会结束,此时所有正在运行的守护线程会被强制杀掉。
Thread rose = new Thread() {
public void run() {
for(int i=0;i<5;i++) {
System.out.println("rose:let me go!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
System.out.println("rose:啊啊啊啊啊AAAAAaaaaa....");
System.out.println("扑通!");
}
};
Thread jack = new Thread() {
public void run() {
while(true) {
System.out.println("jack:you jump!i jump!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
};
rose.start();
/*
* 当参数为true时,将该线程转换为守护线程
* 需要注意:必须在线程启动前进行。
*/
jack.setDaemon(true);
jack.start();
线程的join方法用于协调线程之间的"同步运行"。
线程是并发运行的,感官上是各干各的,这种运行方式称为异步运行。
实际开发中我们也需要让多个线程在特定环节上有顺序的运行,这种操作称为同步运行。
join方法允许调用该方法的线程在该方法所属线程上等待(阻塞),直到其执行完毕后再继续后续工作。
public class JoinDemo {
//图片是否下载完毕
public static boolean isFinish = false;
public static void main(String[] args) {
/*
* java有一个语法要求:
* 当一个方法的局部内部类当中使用了该方法的
* 其他局部变量时,要求这个变量必须是final的
*
* JDK8之后,final关键字可以不写,但是这个
* 语法要求还在。这意味着局部内部类中是不能
* 修改该变量的值的。如果要修改,只能将该变量
* 定义为属性(或挪到该方法之外的其他地方定义)
*/
// final boolean isFinish = false;
Thread download = new Thread() {
public void run() {
System.out.println("down:开始下载图片...");
for(int i=1;i<=100;i++) {
System.out.println("down:"+i+"%");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
System.out.println("down:图片下载完毕!");
isFinish = true;
}
};
Thread show = new Thread() {
public void run() {
try {
System.out.println("show:开始显示文字...");
Thread.sleep(2000);
System.out.println("show:显示文字完毕!");
System.out.println("show:开始显示图片...");
//等待download执行完毕
System.out.println("show:等待下载...");
download.join();
System.out.println("show:等待下载完毕!");
if(!isFinish) {
throw new RuntimeException("图片加载失败!");
}
System.out.println("show:显示图片完毕!");
} catch (Exception e) {
e.printStackTrace();
}
}
};
download.start();
show.start();
}
}
java.util.Collection(集合) 接口
Collection是所有集合的顶级接口,里面规定了所有集合都应当具备的功能方法。
集合与数组相同,可以保存一组元素,并且提供了操作元素的一系列方法,使用更方便。
Collection下面有很多子接口,用来定义不同种类的集合,其中常用的两个为:
java.util.List:线性表,是一个可以存放重复元素的并且有序的集合。
java.util.Set:不可重复集合,大部分实现类是无序的。
元素是否重复是依靠元素自身equals比较的结果。
boolean add(E e) 向当前集合中添加给定元素,成功添加则返回true
int size() 返回当前集合的元素个数
boolean isEmpty() 判断当前集合是否为空集(不含有元素)
clear() 清空集合
Collection c = new ArrayList();
// Collection c = new HashSet();
System.out.println(c);
/*
* boolean add(E e)
* 向当前集合中添加给定元素,成功添加
* 则返回true
*/
c.add("one");
c.add("two");
c.add("three");
c.add("four");
c.add("five");
// c.add("one");
System.out.println(c);
/*
* int size()
* 返回当前集合的元素个数
*/
int size = c.size();
System.out.println("size:"+size);
/*
* boolean isEmpty()
* 判断当前集合是否为空集(不含有元素)
*/
boolean isEmpty = c.isEmpty();
System.out.println("是否为空集:"+isEmpty);
c.clear();
System.out.println("已清空集合");
System.out.println("size:"+c.size());
System.out.println("是否为空集:"+c.isEmpty());
System.out.println(c);
remove() 方法用来删除集合元素
删除时是根据给定元素与集合现有元素进行equals比较结果而定,删除比较为true的
注意:List集合有重复元素时,只删除第一个
public class Point {
private int x;
private int y;
public Point(int x, int y) {
super();
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
/**
* toString方法返回的字符串没有严格的格式
* 要求,可结合实际情况而定,但是通常该字符
* 串应当包含当前对象的属性信息。
*/
public String toString() {
return "("+x+","+y+")";
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Point other = (Point) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
}
Collection c = new ArrayList();
c.add(new Point(1,2));
c.add(new Point(3,4));
c.add(new Point(5,6));
c.add(new Point(7,8));
c.add(new Point(1,2));
System.out.println(c);
Point p = new Point(1,2);
c.remove(p);
System.out.println(c);
boolean contains(Object o) 判断当前集合是否包含给定元素,判断的依据也是依靠元素equals比较的结果
Collection c = new ArrayList();
c.add(new Point(1,2));
c.add(new Point(3,4));
c.add(new Point(5,6));
c.add(new Point(7,8));
System.out.println(c);
Point p = new Point(1,2);
boolean contains = c.contains(p);
System.out.println("是否包含:"+contains);
集合只能存放引用类型元素,并且存放的是元素的引用。
Point p = new Point(1,2);
Collection c = new ArrayList();
c.add(p);
System.out.println("p:"+p);
System.out.println("c:"+c);
p.setX(2);
System.out.println("p:"+p);//(2,2)
System.out.println("c:"+c);//[(?,2)]
集合操作
boolean addAll(Collection c) 将给定集合中的所有元素添加到当前集合中。注:如果当前集合是不可重复集合,那么重复元素不会被添加。
boolean containsAll(Collection c) 判断当前集合是否包含给定集合中的所有元素。
removeAll() 删除当前集合中与给定集合的公有元素。删交集部分。
Collection c1 = new ArrayList();
c1.add("java");
c1.add("c");
c1.add("c++");
System.out.println("c1:"+c1);
Collection c2 = new ArrayList();
c2.add("android");
c2.add("ios");
c2.add("java");
System.out.println("c2:"+c2);
/*
* boolean addAll(Collection c)
* 将给定集合中的所有元素添加到当前集合
* 中。注:如果当前集合是不可重复集合,那么
* 重复元素不会被添加。
*/
c1.addAll(c2);
System.out.println("c1:"+c1);
Collection c3 = new ArrayList();
c3.add("android");
c3.add("c++");
c3.add("php");
System.out.println("c3:"+c3);
/*
* boolean containsAll(Collection c)
* 判断当前集合是否包含给定集合中的所有
* 元素。
*/
boolean contains = c1.containsAll(c3);
System.out.println("全包含:"+contains);
/*
* 删除当前集合中与给定集合的公有元素。
* 删交集部分。
*/
c1.removeAll(c3);
System.out.println("c1:"+c1);
System.out.println("c3:"+c3);
泛型
JDK5推出时还推出了一个特性:泛型
泛型也称为参数化类型,允许我们在使用一个类时指定其中属性,方法参数,方法返回值的类型。使得我们使用时更加方便。
泛型在集合中应用广泛,集合中的泛型是用来规定集合中的元素类型的。
Collection<String> c = new ArrayList<String>();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
// c.add(1);//编译器会检查实参类型是否匹配
//遍历时可以直接用String接收了
for(String str : c) {
System.out.println(str);
}
/*
* 迭代器也支持泛型,指定时与集合指定的
* 类型一致即可
*/
Iterator<String> it = c.iterator();
while(it.hasNext()) {
String str = it.next();//无需再造型
System.out.println(str);
}
遍历集合元素
Collection为所有集合提供了统一的遍历方式:
迭代器模式
方法为:
Iterator iterator()
该方法可以获取一个用于遍历当前集合的迭代器
java.util.Iterator 接口
迭代器接口规定了遍历集合的方式:遵循三步,问,取,删(其中删除元素不是必要操作。)
不同的集合都实现了一个用于遍历自身元素的迭代器实现类,并通过iterator()方法返回。
Collection c = new ArrayList();
c.add("one");
c.add("#");
c.add("two");
c.add("#");
c.add("three");
c.add("#");
c.add("four");
c.add("#");
c.add("five");
System.out.println(c);
Iterator it = c.iterator();
/*
* 迭代器提供的遍历方法:
* boolean hasNext()
* 判断集合是否还有元素可以遍历
*
* E next()
* 获取集合下一个元素
*
*/
while(it.hasNext()) {
String str = (String)it.next();
System.out.println(str);
if("#".equals(str)) {
/*
* 迭代器遍历过程中不得通过
* 集合的方法增删元素,否则
* 遍历过程会抛出异常
*/
// c.remove(str);
it.remove();
}
}
System.out.println(c);
增强for循环
DK5版本推出后,推出了一个特性:增强for循环
该特性使得我们遍历数组与遍历集合的语法一致了增强for循环也称为新循环,它不取代传统for循环的工作,仅用来使用相同的语法遍历集合或数组。
String[] array = {"one","two","three","four"};
for(int i=0;i<array.length;i++) {
String str = array[i];
System.out.println(str);
}
/*
* 新循环语法是编译器认可,而不是虚拟机
* 编译器在编译过程中会改为普通for循环
* 来遍历数组
*/
for(String str:array) {
System.out.println(str);
}
使用新循环遍历集合
Collection c = new ArrayList();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
System.out.println(c);
//新循环就是迭代器遍历,不要增删元素
for(Object o:c) {
String s = (String)o;
System.out.println(s);
}
java.util.List(List集合)
List是可重复集,并且有序,提供了一套根据下标操作元素的方法
常用实现类:
java.util.ArrayList
数组实现,查询性能好,增删性能差
java.util.LinkedList
链表实现,增删性能好,查询性能差
对性能不是极端苛刻下,一般使用ArrayList即可
E get(int index) 获取指定下标处对应的元素
E set(int index, E e) 将给定元素设置到指定位置上,返回值为
- 该位置原有元素
- 替换元素操作
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
System.out.println(list);
/*
* E get(int index)
* 获取指定下标处对应的元素
* String s = arr[i];
*/
String str = list.get(1);
System.out.println(str);
for(int i=0;i<list.size();i++) {
str = list.get(i);
System.out.println(str);
}
/*
* E set(int index, E e)
* 将给定元素设置到指定位置上,返回值为
* 该位置原有元素
* 替换元素操作
*/
//[one,2,three,four,five]
String old = list.set(1, "2");
System.out.println(list);
System.out.println(old);
/*
* 在不创建新集合的前提下,将集合元素
* 倒序
*/
for(int i=0;i<list.size()/2;i++) {
String s = list.get(i);
s = list.set(list.size()-1-i,s);
list.set(i, s);
}
//[five,four,three,2,one]
System.out.println(list);
List重载了一对add,remove方法
void add(int index,E e) 将给定元素插入到指定位置
E remove(int index) 删除并返回指定位置对应的元素
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
System.out.println(list);
/*
* void add(int index,E e)
* 将给定元素插入到指定位置
*/
//[one, 2, two, three, four, five]
list.add(1, "2");
System.out.println(list);
/*
* E remove(int index)
*删除并返回指定位置对应的元素
*/
String old = list.remove(2);
System.out.println(list);
System.out.println(old);
List subList(int start,int end) 获取当前集合中指定范围内的子集
List<Integer> list = new ArrayList<>();
for(int i=0;i<10;i++) {
list.add(i);
}
System.out.println(list);
List<Integer> subList
= list.subList(3, 8);
System.out.println(subList);
//将子集每个元素扩大10倍
for(int i=0;i<subList.size();i++) {
int n = subList.get(i);
n = n * 10;
subList.set(i, n);
}
System.out.println(subList);
/*
* 对子集元素的操作就是对原集合对应元素
* 的操作
*/
System.out.println(list);
//删除当前集合2-8
list.subList(2,9).clear();
System.out.println(list);
排序集合
集合的工具类:java.util.Collections
其提供了很多便于操作集合的方法,其中的sort方法可以对集合进行自然排序(从小到大)
需要注意,该方法仅对List集合进行排序
Random random = new Random();
List<Integer> list = new ArrayList<>();
for(int i=0;i<10;i++) {
list.add(random.nextInt(100));
}
System.out.println(list);
Collections.sort(list);
System.out.println(list);
数组转换为集合
数组的工具类Arrays提供了一个静态方法:
List asList(T[] t) 可以将给定的数组转换为一个List集合
String[] array = {"one","two","three","four"};
System.out.println(Arrays.toString(array));
List<String> list = Arrays.asList(array);
System.out.println(list);
System.out.println(list.size());
/*
* 通过数组转换的集合,对该集合的元素操作
* 就是对原数组对应元素的操作
*/
list.set(1, "2");
System.out.println(list);
System.out.println(Arrays.toString(array));
/*
* 数组转换来的集合是不能做增删元素的
* 操作的,会抛出不支持的异常。
* 因为数组是定长的,不能增删元素。
*/
// list.add("five");
/*
* 如果想增删元素,需要自行创建一个集合
*/
List<String> list2= new ArrayList<>(list);
System.out.println("list2:"+list2);
list2.add("five");
System.out.println("list2:"+list2);
java.util.Queue 队列
队列接口继承自Collection,因此队列有集合特性。
队列的特点是存取元素必须遵循先进先出。
常用实现类:java.util.LinkedList
Queue<String> queue = new LinkedList<>();
//入队操作
queue.offer("one");
queue.offer("two");
queue.offer("three");
queue.offer("four");
queue.offer("five");
System.out.println(queue);
//出队,获取后该元素从队列中被移除
String str = queue.poll();
System.out.println(str);
System.out.println(queue);
//引用队首元素,获取后元素不会被移除
str = queue.peek();
System.out.println(str);
System.out.println(queue);
/*
* 使用新循环(迭代器)遍历队列
* 遍历不影响队列元素
*/
for(String s : queue) {
System.out.println(s);
}
System.out.println(queue);
/*
* 使用poll方法遍历队列
*/
while(queue.size()>0) {
String e = queue.poll();
System.out.println(e);
}
System.out.println(queue);
栈结构
栈结构可以保存一组元素,存取元素遵循先进后出栈结构可以通过双端队列Deque实现。其为栈结构专门提供了方法:push(入栈),pop(出栈)
Deque<String> stack = new LinkedList<>();
stack.push("one");
stack.push("two");
stack.push("three");
stack.push("four");
System.out.println(stack);
String str = stack.pop();
System.out.println(str);
System.out.println(stack);
for(String s : stack) {
System.out.println(s);
}
System.out.println(stack);
while(stack.size()>0) {
str = stack.pop();
System.out.println(str);
}
System.out.println(stack);
java.util.Map 查找表
Map体现的样子是一个多行两列的表格,左列称为key,右列称为value。
Map总是根据key来获取对应的value。
Map有一个要求,key不允许重复,并且总是成对保存数据的。
常用实现类:java.util.HashMap,称为散列表或哈希表。当今查询速度最快的数据结构。
TreeMap 二叉树
V put(K k,V v)
将给定的键值对存入Map中。返回值为被替换的value(前提是是用了相同的key时才会替换,否则返回值为null)
V get(Object key)
根据给定的key获取对应的value,如果给定的key不存在,则返回值为null
V remove(Object key)
删除给定的key所对应的这组键值对返回值为对应的value
Map<String,Integer> map = new HashMap<>();
/*
* V put(K k,V v)
* 将给定的键值对存入Map中。返回值为
* 被替换的value(前提是是用了相同的key
* 时才会替换,否则返回值为null)
*/
map.put("语文", 99);
map.put("数学", 98);
map.put("英语", 97);
map.put("物理", 96);
map.put("化学", 99);
System.out.println(map);
Integer num = map.put("数学", 55);
System.out.println(map);
System.out.println(num);
/*
* V get(Object key)
* 根据给定的key获取对应的value,如果
* 给定的key不存在,则返回值为null
*/
num = map.get("英语");
System.out.println(num);
num = map.get("体育");
System.out.println(num);
/*
* V remove(Object key)
* 删除给定的key所对应的这组键值对
* 返回值为对应的value
*/
num = map.remove("英语");
System.out.println(map);
System.out.println(num);
int size = map.size();
System.out.println(size);
boolean ck = map.containsKey("数学");
System.out.println("包含key:"+ck);
boolean cv = map.containsValue(55);
System.out.println("包含value:"+cv);
map.clear();
System.out.println(map);
Map的遍历
Map支持三种遍历方式:
1:遍历所有的key
2:遍历所有的键值对
3:遍历所有的value(相对不常用)
Set keySet() 遍历所有的key
将当前Map中所有的key以一个Set集合形式返回。遍历该集合等于遍历所有的key
Set entrySet() 遍历每一组键值对
将当前Map中一个组键值对以一个Entry实例存入Set集合,并将该集合返回。
Collection values() 遍历每一个value
Map<String,Integer> map = new HashMap<>();
map.put("语文", 99);
map.put("数学", 98);
map.put("英语", 97);
map.put("物理", 96);
map.put("化学", 99);
System.out.println(map);
/*
* 遍历所有的key
* Set keySet()
* 将当前Map中所有的key以一个Set集合形式
* 返回。遍历该集合等于遍历所有的key
*/
Set<String> keySet = map.keySet();
for(String key : keySet) {
System.out.println("key:"+key);
}
/*
* 遍历每一组键值对
* Set entrySet()
* 将当前Map中一个组键值对以一个Entry实例
* 存入Set集合,并将该集合返回。
*
* java.util.Map.Entry的每一个实例用于
* 表示Map中的一组键值对
*/
Set<Entry<String,Integer>> entrySet
= map.entrySet();
for(Entry<String,Integer> e: entrySet) {
String key = e.getKey();
Integer value = e.getValue();
System.out.println(key+":"+value);
}
/*
* 遍历每一个value
* Collection values()
*/
Collection<Integer> values
= map.values();
for(Integer value : values) {
System.out.println("value:"+value);
}
lambda
lambda表达式 DK8之后推出的新特性
lambda使得我们可以"以函数式编程"。
lambda最直接的感受是可以以更精简的代码完成匿名内部类的创建。
lambda的语法:
(参数列表)->{
方法体
}
只有接口中含有一个抽象方法时,才可以使用lambda创建!
Runnable r1 = new Runnable() {
public void run() {
System.out.println("hello");
}
};
Runnable r2 = ()->{
System.out.println("hello");
};
/*
* 如果方法体中只有一句代码时,方法体
* 对应的"{}"是可以忽略不写的
*/
Runnable r3
= ()->System.out.println("hello");
Thread t = new Thread(
()->System.out.println("hello!")
);
t.start();
List<String> list = new ArrayList<>();
list.add("Java");
list.add("MySQL");
list.add("HTML");
// Comparator<String> c = new Comparator<String>() {
// public int compare(String o1, String o2) {
// return o1.length()-o2.length();
// }
// };
// Comparator<String> c = (o1,o2)->{
// return o1.length()-o2.length();
// };
//如果lambda可以忽略{},那么return关键字也要忽略
Collections.sort(
list,
(o1,o2)->o1.length()-o2.length()
);
System.out.println(list);
java.util.Calendar 日历类
Calendar是常用的操作时间的API,本身是一个抽象类,定义了操作时间的相关方法。常用实现类为java.util.GregorianCalendar,即:阳历获取Calendar实例可以使用Calendar的静态方法getInstance,大部分地区返回的都是阳历实现。
Date getTime() 将当前Calendar表示的日期以Date实例形式返回。
void setTime(Date date) 调整当前Calendar表示给定的Date所表示的日期
Calendar calendar = Calendar.getInstance();
System.out.println(calendar);
/*
* Date getTime()
* 将当前Calendar表示的日期以Date实例
* 形式返回。
*/
Date date = calendar.getTime();
System.out.println(date);
/*
* void setTime(Date date)
* 调整当前Calendar表示给定的Date所
* 表示的日期
*/
calendar.setTime(date);
int get(int field) 获取指定时间分量所对应的值。
时间分量是一个int值,Calendar中定义了大量的常量进行了表示。
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
//月从0开始,0表示1月。
int month = calendar.get(Calendar.MONTH)+1;
/*
* 和"天"相关的常量
* DATE:月中的天
* DAY_OF_MONTH:月中的天,与DATE一致
* DAY_OF_YEAR:年中的天
* DAY_OF_WEEK:周中的天,星期几
*
*/
int date = calendar.get(Calendar.DATE);
System.out.println(year+"-"+month+"-"+date);
int h = calendar.get(Calendar.HOUR);
int m = calendar.get(Calendar.MINUTE);
int s = calendar.get(Calendar.SECOND);
System.out.println(h+":"+m+":"+s);
//今天是今年的第几天?
System.out.println(
"今年已经过了:"+
calendar.get(Calendar.DAY_OF_YEAR)+"天");
//今天周几? 一周的第一天是周日
int dow = calendar.get(Calendar.DAY_OF_WEEK)-1;
String[] data = {"日","一","二","三","四","五","六"};
System.out.println("周"+data[dow]);
/*
* 获取指定时间分量所允许的最大值
* 参照时间为当前Calendar表示的时间
*/
int d = calendar.getActualMaximum(
Calendar.DAY_OF_YEAR);
System.out.println(d);
void set(int field,int value) 调整当前Calendar指定的时间分量为给定的值
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.getTime());
//调整年
calendar.set(Calendar.YEAR,2008);
//调整月
calendar.set(Calendar.MONTH, Calendar.AUGUST);
//调整日
calendar.set(Calendar.DATE, 8);
System.out.println(calendar.getTime());
void add(int field,int amount) 对指定的之间分量加上给定的值,若给定的值是负数,则是减去
/*
* 查看3年2月零25天以后那周的周三是哪天?
*/
Calendar calendar = Calendar.getInstance();
//加3年
calendar.add(Calendar.YEAR, 3);
//加2个月
calendar.add(Calendar.MONTH, 2);
//加25天
calendar.add(Calendar.DAY_OF_YEAR, 25);
System.out.println(calendar.getTime());
calendar.set(Calendar.DAY_OF_WEEK, 4);
System.out.println(calendar.getTime());
java.util.Date
Date的每一个实例用于表示一个时间,内部维护一个long值,该值记录的是自1970年元旦到当前Date表示的时间之间所经过的毫秒。
由于Date存在时区和千年虫问题,因此操作时间的一系列API方法几乎都"过时"了,不再建议使用。
因此我们现在使用Date仅作为表示时间使用。
date.getYear(); 获取Date内部的毫秒值
Date date = new Date();
System.out.println(date);
// date.getYear();
//获取Date内部的毫秒值
long time = date.getTime();
System.out.println(time);
date.setTime(0);
System.out.println(date);
java.text.SimpleDateFormat可以按照指定的日期格式将String与Date之间相互转换。
String format(Date date) 将给定的Date转换为String
Date now = new Date();
System.out.println(now);
/*
* 2019-12-20 10:25:23
* yyyy-MM-dd HH:mm:ss
*/
SimpleDateFormat sdf
= new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss E a");
/*
* String format(Date date)
* 将给定的Date转换为String
*/
String str = sdf.format(now);
System.out.println(str);
将一个String按照指定的日期格式解析为Date
Date parse(String str) 将给定的字符串按照指定的日期格式解析 String->Date
String str = "2008-08-08 20:08:08";
SimpleDateFormat sdf
= new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss"
);
/*
* Date parse(String str)
* 将给定的字符串按照指定的日期格式解析
* String->Date
*/
Date date = sdf.parse(str);
System.out.println(date);
练习1:
程序启动后要求用户输入自己的生日:yyyy-MM-dd经过程序计算后输出到今天为止一共活了多少天再经过计算输出其出生10000天纪念日,格式同上
Scanner scanner = new Scanner(System.in);
System.out.println("请输入您的生日:");
String birthStr = scanner.nextLine();
SimpleDateFormat sdf
= new SimpleDateFormat("yyyy-MM-dd");
Date birth = sdf.parse(birthStr);
Date now = new Date();
long time = now.getTime()-birth.getTime();
time = time/1000/60/60/24;
System.out.println("恭喜您一共活了"+time+"天,请继续保持!");
time = birth.getTime()+1000L*60*60*24*10000;
Date date = new Date(time);
String line = sdf.format(date);
System.out.println("您出生10000天的纪念日为:"+line);
练习2:
计算促销日期
程序开始后要求输入某商品生产日期:yyyy-MM-dd 然后再输入保质期的天数,经过计算输出促销日期,格式同上促销日期计算规则:商品过期日前两周的周三
Scanner scanner = new Scanner(System.in);
System.out.println("请输入商品的生产日期:");
String dateStr = scanner.nextLine();
System.out.println("请输入保质期的天数:");
int days = scanner.nextInt();
SimpleDateFormat sdf
= new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse(dateStr);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
//计算过期日
calendar.add(Calendar.DAY_OF_YEAR, days);
//计算促销日
calendar.add(Calendar.DAY_OF_YEAR, -14);
calendar.set(Calendar.DAY_OF_WEEK, 4);
date = calendar.getTime();
String line = sdf.format(date);
System.out.println("促销日期为:"+line);
反射机制
反射是一种动态机制,允许java程序在运行期间再确定对象的实例化,方法的调用,属性的操作等。
反射可以提高代码的灵活性,但是会有更多的性能开销和较低的运行效率。因此不能过度依赖反射。
/**
* 用于测试反射机制
* @author Administrator
*
*/
public class Person {
public void say() {
System.out.println("person:hello!");
}
public void sayHi() {
System.out.println("person:Hi!");
}
public void sayHi(String name) {
System.out.println("person:hi!我是"+name);
}
public void sayHi(String name,int age) {
System.out.println("person:Hi!我是"+name+",今年"+age);
}
private void dosome() {
System.out.println("我是私有方法!!!!");
}
}
/*
* Class类,称为类的类对象
* JVM中每个被加载的类都有且只有一个
* Class的实例与之对应,通过获取一个类
* 的类对象我们可以得知这个类的一切信息,
* 比如类名,有哪些方法,属性,构造器等
* 并且可以动态的操作它们。
*
* 所以反射的第一步就是获取类对象,而获取
* 一个类的类对象有三种方式:
* 1:类名.class
* 例如:
* Class cls = String.class;
* Class cls = int.class;
*
* 2:Class.forName(String className)
* 例如:
* Class cls
* = Class.forName("java.lang.String");
*
* 3:类加载器ClassLoader
*/
//测试String
// Class cls = String.class;
// Class cls = Class.forName("raflect.Person");
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个类型:");
String className = scanner.nextLine();
Class cls = Class.forName(className);
String name = cls.getName();
System.out.println(name);
//通过类对象获取所有的方法
// Method[] methods = cls.getMethods();
//获取当前类自己定义的所有方法(不含有继承的)
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods) {
System.out.println(method.getName());
}
利用反射实例化对象
//实例化Person
Object p = new Person();
System.out.println(p);
//1获取类对象
// Class cls = Class.forName("raflect.Person");
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个类型:");
String className = scanner.nextLine();
Class cls = Class.forName(className);
/*
* 2实例化
* 此种方式实例化要求必须有无参构造器
* java.util.ArrayList
* java.util.Date
* java.util.HashMap
* java.lang.String
*
*/
Object obj = cls.newInstance();
System.out.println(obj);
利用反射机制调用方法
Person p = new Person();
p.sayHi();
Scanner scanner = new Scanner(System.in);
System.out.println("请输入类名");
String className = scanner.nextLine();
//1实例化
Class cls = Class.forName(className);
Object o = cls.newInstance();
//2调用方法
System.out.println("请输入方法名:");
String methodName = scanner.nextLine();
Method method = cls.getMethod(methodName);
method.invoke(o);
调用有参数的方法
//实例化
Class cls = Class.forName("raflect.Person");
Object o = cls.newInstance();
//调用方法
//sayHi(String name)
Method m1 = cls.getMethod(
"sayHi", String.class);
m1.invoke(o, "张三");
//sayHi(String name,int age)
Method m2 = cls.getMethod(
"sayHi", String.class,int.class);
m2.invoke(o,"李四",22);
// Person p = new Person();
// p.dosome();//编译失败
//利用反射在类的外部调用其私有方法
Method m3 = cls.getDeclaredMethod("dosome");
m3.setAccessible(true);//强制访问
m3.invoke(o);