JAVA常用基础API(经典实例代码)
程序员文章站
2023-11-20 23:35:52
API文档注释写法实例:package apidoc;/** * 在类上面写文档注释,是用来说明当前类的设计目的、功能 * @author ta * @version 1.0 * @see java.lang.String * @since JDK1.0 */public class APIDocDemo {/** * sayHello中使用的问候语 */public static final String INFO = "hello";/** * 对给定用户...
API文档注释写法
实例:
package apidoc;
/**
* 在类上面写文档注释,是用来说明当前类的设计目的、功能
* @author ta
* @version 1.0
* @see java.lang.String
* @since JDK1.0
*/
public class APIDocDemo {
/**
* sayHello中使用的问候语
*/
public static final String INFO = "hello";
/**
* 对给定用户添加问候语
* @param name 给定的用户名
* @return 添加了问候语的字符串
*/
public String sayHello(String name) {
return "你好!"+name;
}
}
--------String 字符串的API--------
- JVM对String有一个优化措施,即:常量池
- java推荐我们使用"字面量"形式创建字符串对象,因为当
- 我们这样做时虚拟机会首先检查常量池中是否有创建过该内
- 容的字符串对象,若有,则直接重用对象。
- 这样做可以减少内存中出现大量内容一样的字符串对象而带来的资源消耗等问题。
- 频繁修改字符串代码的性能损耗(GC循环回收释放)
char charAt(int index)
返回当前字符串中给定位置处对应的字符
实例:
String str = "thinking in java";
char c = str.charAt(9);
System.out.println(c);//i
int length()
该方法返回当前字符串的长度(字符个数)
实例:
String str = "我爱java";
System.out.println(str.length());
int indexOf(String str)
查找给定字符串在当前字符串中的位置(首字母出现的位置, 第一次出现的位置。
若当前字符串不包含该内容则返回值为-1
实例:
String str = "thinking in java";
int index = str.indexOf("in");
System.out.println(index); //2 i字母首次出现的下标
String substring(int start,int end)
截取当前字符串中指定范围内的内容
注:在java API当中通常使用两个数字表示范围时,
都是 "含头不含尾"的
实例:
String str = "www.tedu.cn";
//截取"tedu"
String sub = str.substring(4, 8);
System.out.println(sub);
//一个参数为从指定位置截取到末尾
sub = str.substring(4); //tedu.cn
System.out.println(sub);
boolean startsWith(String str)
和 boolean endsWith(String str)
判断字符串是否是以给定字符串(前缀、后缀)开始或结尾的
实例:
String str = "thinking in java";
boolean starts = str.startsWith("thin");
System.out.println(starts);
boolean ends = str.endsWith("ava");
System.out.println(ends);
String toUpperCase()
和 String toLowerCase()
将当前字符串中的英文部分转换为全大写或全小写
实例:
String str = "LOVEJava";
String upper = str.toUpperCase();
System.out.println(upper);
String lower = str.toLowerCase();
System.out.println(lower);
String trim()
去除当前字符串两边的空白字符,字符串中间存在空格无法去除
实例:
String str = " hello ";
String trim = str.trim();
System.out.println(str); //" hello "
System.out.println(trim); //"hello"
static String valueOf()
该方法用若干的重载,参数囊括基本类型及引用类型。
但是方法的作用是同样的,将给定的内容转换为字符串形式。比较常用的是将基本类型转换为字符串。
实例:
int a = 1234;
String str = String.valueOf(a);
System.out.println(str);
str = a+"";
System.out.println(str);
double d = 123.123;
str = String.valueOf(d);
System.out.println(str);
String支持正则表达式的方法一:boolean matches(String regex)
使用给定的正则表达式验证当前字符串是否满足各式要求。
满足则返回true,不满足则返回false
需要注意!!!!!
给定的正则表达式,无论是否添加了边界匹配符(^....$)
都是做全匹配验证。
实例:
String mail = "fancq@tedu.cn";
/*
* [a-zA-Z0-9_]+@[a-zA-Z0-9]+(\.[a-zA-Z]+)+ 判断是否为邮箱的正则表达式
*/ \W+@(\W[^_])+(\\.[a-zA-Z]+)+ // \w不包含下划线 _
String regex = "[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\\.[a-zA-Z]+)+";
boolean match = mail.matches(regex);
if(match) {
System.out.println("是邮箱");
}else {
System.out.println("不是邮箱");
}
String支持正则表达式方法二:String[] split(String regex)
将当前字符串按照满足正则表达式的部分进行拆分,将
拆分后的内容以String数组形式返回。
实例:
String str = "abc123def456ghi";
//按照数字部分拆分
String[] array = str.split("[0-9]+");
System.out.println(array.length);
System.out.println(Arrays.toString(array));
/*
* 在拆分时若连续匹配到拆分内容,则中间会拆分
* 出一个空字符串。但是在字符串末尾连续匹配的
* 话拆分的空字符串会全部忽略
*/
// str = "........1.2.3.4.5.6.7.8"; 有空字符串
// str = "1.2.3.4.........5.6.7.8"; 有空字符串
str = "1.2.3.4.5.6.7.8........";
array = str.split("\\.");
System.out.println(array.length);
System.out.println(Arrays.toString(array));
/*
* 图片重命名
* 1.jpg
*/
String image = "1.jpg";
array = image.split("\\.");
image = System.currentTimeMillis()+"."+array[1]; //时间命名
System.out.println(image);
String支持正则表达式方法三:String replaceAll(String regex,String str)
将当前字符串中满足正则表达式的部分替换为给定内容
实例:
String str = "abc123def456ghi";
/*
* 将当前字符串中数字部分替换为"#NUMBER#"
*/
str = str.replaceAll("[0-9]+", "#NUMBER#");
System.out.println(str);
String regex = "(wqnmlgb|sb|nc|mdzz|cnm|djb)";
String message = "wqnmlgb!你个sb!你怎么这么的nc!cnm!你就是一个djb!";
message = message.replaceAll(regex, "***");
System.out.println(message);
StringBuilder
* 由于String的设计不适合频繁修改,对此java提供了一个
* 用于编辑字符串内容的类:StringBuilder
* StringBuilder内部维护了一个可变的字符数组,不会每次
* 修改都来创建新对象,从而降低资源开销,提高执行效率。
* 其提供了方便编辑字符串内容的相关方法,包含:
* 增,删,改,插等常见操作。
实例:
String str = "好好学习java";
/*
* 常见构造方法:
* StringBuilder():无参,默认表示空字符串
* StringBuilder(String str):表示给定内容
*/
StringBuilder builder = new StringBuilder(str);
/*
* 好好学习java
* 好好学习java,为了找个好工作!
*
* append():向当前字符串末尾追加给定内容
*/
builder.append(",为了找个好工作!");
//获取builder内部编辑后的字符串
String s = builder.toString();
System.out.println(s);
/*
* 好好学习java,为了找个好工作!
* 好好学习java,就是为了改变世界!
* (含头不含尾)
* replace():替换指定范围内的字符串
*/
builder.replace(9, 16, "就是为了改变世界");
System.out.println(builder);
/*
* 好好学习java,就是为了改变世界!
* ,就是为了改变世界!
*
* delete():删除指定范围内的字符串
*/
builder.delete(0, 8);
//删除指定下标处的字符
builder.deleteCharAt(5);
System.out.println(builder);
/*
* ,就是为了改变世界!
* 活着,就是为了改变世界!
*
* insert():在当前字符串中插入指定内容
*/
builder.insert(0, "活着");
System.out.println(builder);
实例:
public void StrBuilder() {
StringBuilder builder=new StringBuilder("好好学习!");
//增
builder.append("天天向上!");
//删
//builder.delete(0, 2);
builder.deleteCharAt(5); //删除指定下标的字符
//改
builder.replace(0, 4, "童话里的故事都是骗人的");
//插
builder.insert(10,",癞蛤蟆想吃天鹅肉");
//查(打印)
System.out.println(builder.toString());
}
* StringBuilder修改字符串内容的性能 //执行效率高,线程不安全
实例:
StringBuilder builder = new StringBuilder("a");
for(int i=0;i<10000000;i++) {
builder.append("a");
}
System.out.println("执行完毕!");
--------包装类API--------
Byte Short Integer Long Float Double Char Boolean
* 由于java中8个基本类型不能直接参与面向
* 对象的开发(不具有面向对象对应特性)。
* 因此java为这8个基本类型分别提供了对应
* 的引用类型,我们称它们为:包装类
/*
* 基本类型转换为包装类
*/
//推荐使用静态方法valueOf进行转换
Integer i1 = Integer.valueOf(1);
//Double则是直接new实例
Double d1 = Double.valueOf(1.0);
/*
* 包装类转换为基本类型
*/
int d = i1.intValue();
double dou = i1.doubleValue();
/*
* 数字的包装类提供了两个常量
* MAX_VALUE,MIN_VALUE分别表示对应基本
* 类型的取值范围。
*/
//获取int最大值
int max = Integer.MAX_VALUE;
int min = Integer.MIN_VALUE;
byte bmax = Byte.MAX_VALUE;
byte bmin = Byte.MIN_VALUE;
* JDK1.5推出时,推出了一个新的特性:自动拆装箱。
* 该特性是编译器认可,而非虚拟机。编译器在编译源代码时
* 发现基本类型和引用类型相互赋值时,会自动添加代码补全
* 它们之间的转换操作。
int d = 123;
/*
* 这里触发了编译器自动装箱特性:
* 编译器会补全代码,将基本类型转换为包装类,下面
* 的代码在编译后的class文件中为:
* Integer in = Integer.valueOf(d);
*/
Integer in = d;
/*
* 触发自动拆箱特性,编译器会改为:
* d = in.intValue();
*/
d = in;
* 包装类都支持一个静态方法:parseXXX(String str)
* 该方法的作用是可以将字符串转换为对应的基本类型,但是
* 前提是该字符串的内容正确描述了基本类型可以保存的值。
String str = "123";
/*
* 当给定的字符串不能转换为对应的基本类型时,
* 会抛出NumberFormatException(数字格式异常)。
*/
int d = Integer.parseInt(str);
System.out.println(d);//?
double dou = Double.parseDouble(str);
System.out.println(dou);//?
* Byte、Short、Integer、Long、 包装类提供了一个进制转换的方法:
* public static int parseInt(String s,int radix)
* s - 包含要解析的整数表示形式的 String
* radix - 解析 s 时使用的基数(进制)。
/**
* 解析7进制,如:7进制"123"解析成十进制为 66.
* 注意:不能使用java API解析
*/
public void parse7(){
String str="123";
int num=Integer.parseInt(str,7);
long numl=Long.parseLong(str, 7);
System.out.println(num); //66
System.out.println(numl); //66
}
--------File文件操作相关API --------
File
* 使用File创建一个文件
* 在当前目录下创建名为:test.txt的文件
File file = new File("./test.txt");
* boolean exists()
* 判断当前File表示的文件或目录是否已经存在
* 创建该文件时需要注意,创建的文件所在的目录必须存在,
* 否则会抛出异常。
file.createNewFile();
'./': 表示当前项目工程根目录;' /': 表示磁盘根目录
* 删除一个文件
* 将当前项目目录下的test.txt文件删除在相对路径中,"./"是可以忽略不写的,
* 默认就是从当前目录(工程根目录)开始的。
file.delete();
* 在当前目录下创建一个名为demo的目录
创建目录:File dir = new File("demo");
dir.mkdir(); //mkdir()方法要求创建的目录所在的父目录必须存在。
* 将当前目录下的demo目录删除
* delete方法在删除目录时要求该目录必须是一个空目录。
* 在当前目录下创建a/b/c/d/e/f目录
File dir = new File("a/b/c/d/e/f");
dir.mkdirs(); // mkdirs()方法则会将不存在的父目录一同创建出来。
删除目录:dir.delete(); //删除多级目录需要使用递归方法
递归方法: public static void delete(File f) {
if(f.isDirectory()) {
//先将该目录清空
File[] subs = f.listFiles();
for(int i=0;i<subs.length;i++) {
File sub = subs[i];
//先删除当前子项
/*
* 递归调用
* 一个方法内部调用自己方法的现象称为
* 递归调用。
* 使用递归要注意:
* 1:递归调用一定是在一个分支语句中控制的,
* 如果方法内部必定调用当前方法则是死循环。
* 2:递归调用的层数不宜过多,因为会大量的
* 消耗内存,并且执行速度慢。(能不用递归
* 解决问题尽量避免使用)
*
*/
delete(sub);
}
}
f.delete();
}
/** 删除f目录下的所有文件以及f目录 */
public static void delete(File f) {
if(f.isDirectory()) {
File[] sub=f.listFiles();
for (File file : sub) {
delete(file);
}
}
f.delete();
System.out.println("删除成功!");
}
* java.io.File
* File用来表示文件系统中的一个文件或目录
* 使用File可以:
* 1:访问其表示的文件或目录的属性信息(名字,大小等信息)
* 2:创建或删除文件及目录
* 3:访问一个目录中的子项
* 但是File不能访问文件数据。
* 访问当前项目目录下的demo.txt文件
*
* 相对路径的优势时可以跨平台跨环境,但是相对
* 的路径要根据实际运行环境而定。
* 在eclipse中运行java程序时,eclipse指定的
* "当前目录./"是当前程序所在的项目目录。
* 这里是JSD1808_SE这个目录。
File file = new File("./demo.txt");
//获取名字
String name = file.getName();
//获取长度(单位是字节)
long len = file.length();
//是否可读
boolean cr = file.canRead();
//是否可写
boolean cw = file.canWrite();
//是否隐藏
boolean ih = file.isHidden();
* 获取一个目录中的所有子项
* boolean isFile() 判断是否为文件
* boolean isDirectory() 判断是否为目录
* File[] listFiles() 获取当前文件夹下的所有子文件。
if(dir.isDirectory()) {
File[] subs = dir.listFiles();
for(int i=0;i<subs.length;i++) {
System.out.println(subs[i].getName());
}
}
* 重载的ListFiles方法,允许我们传入一个文件过滤器然后将目录中满足过滤器要求的子项返回。
public static void main(String[] args) {
/*
* 获取当前目录下所有名字以"."开头的子项
*/
File dir = new File(".");
/*
* FileFilter 文件过滤器
*/
FileFilter filter = new FileFilter() {
public boolean accept(File file) {
String name = file.getName();
System.out.println("正在过滤:"+name);
return name.startsWith(".");
}
};
/*
* 我们将文件过滤器传递给listFiles方法,在当前
* 代码中看似没有显示的调用过文件过滤器的accept
* 方法,但是实际上listFiles方法在执行过程中会
* 回调我们传递的文件过滤器的accept方法来完成其
* 操作。
* 而这种开发模式称为:回调模式
* java 23种设计模式
*/
File[] subs = dir.listFiles(filter);
System.out.println(subs.length);
for(int i=0;i<subs.length;i++) {
System.out.println(subs[i].getName());
}
}
RandomAccessFile
* 读取文件数据
RandomAccessFile raf= new RandomAccessFile("raf.dat","r");
/*
* int read()
* 从文件中读取1个字节,并以int形式返回。若
* 返回值为-1,则表示读取到了文件末尾
* 00000000 00000000 00000000 00000001
*/
int d = raf.read(); //读到文件末尾返回-1
* java.io.RandomAccessFile
* 专门用来读写文件数据的API,RAF基于指针对文件数据读写。
* 可以移动指针读写任意位置,所以可以灵活的对文件数据进行
* 编辑工作。
* 创建RAF时有两种常见模式:
* r:只读模式
* rw:读写模式
* 向当前目录中的文件raf.dat中写入一个字节
* 创建RandomAccessFile时常用的构造方法:
* RandomAccessFile(String path,String mode)
* RandomAccessFile(File file,String mode)
* mode:模式,可以用"rw","r"
* 注意,如果是只读模式时,文件不存在时会抛出异
* 常,若读写模式,则会在不存在时自动将该文件创
* 建出来
RandomAccessFile raf = new RandomAccessFile("raf.dat","rw");
* long getFilePointer()
* 获取当前RAF的指针位置
* void seek(long pos)
* 移动指针到指定位置
raf.seek(0); //将指针移动到文件开始,表示移动指针到第几个字节上
* RAF也提供了读取基本类型数据的方法
* int readInt()
* 连续读取4字节并还原int值。若在读取4个字节的过程
* 中发现读取到文件末尾,则直接抛出异常:
* EOFException EOF:end of file
* 注:这个方法并不会以返回-1表示文件末尾,而是直接
* 抛异常,原因在于这里是真实读取4字节的int值,那么
* 在读取的数据中是可能读取到int型-1的,所以就不能
* 再用int型-1表示文件末尾了。
实例:
int d = raf.readInt();
long l = raf.readLong();
double dou = raf.readDouble();
raf.writeInt(12);
raf.writeLong(456);
raf.writeDouble(3.14);
* 提高每次读写的数据量,减少实际发生的读写次数,可以
* 提高读写效率。
* 单字节读写的模式,一般称为:随机读写
* 一组一组字节读写的模式一般称为:块读写
* RandomAccessFile提供了块读写的方法:
* int read(byte[] data)
* 一次性读取给定字节数组总长度的字节量并存入到
* 该数组中,返回值为实际读取到的字节量长度,若返回
* 值为-1.则表示文件末尾(本次没有读取到任何字节)
* void write(byte[] data)
* 一次性将给定的字节数组中所有字节写出
* void write(byte[] data,int index,int len)
* 将给定字节数组从下标index处开始的连续len个字节
* 一次性写出
实例:
RandomAccessFile src = new RandomAccessFile("movie.mp4","r");
RandomAccessFile desc= new RandomAccessFile("movie_cp.mp4","rw");
//10kb
byte[] data = new byte[1024*10];
//每次实际读取到的字节量
int len = -1;
long start = System.currentTimeMillis();
while((len = src.read(data))!=-1) {
desc.write(data,0,len);
//desc.write(data);
}
long end = System.currentTimeMillis();
System.out.println("复制完毕!耗时:"+(end-start)+"ms");
src.close();
desc.close();
* 读取一个字符
* 问题:应当读取几个字节?
* 除非了解该字符集编码规则。否则无法确定应当
* 读取多少个字节。
* RAF也有length方法,获取其读写的文件的长度
实例:
RandomAccessFile raf= new RandomAccessFile("raf.txt","r");
byte[] data = new byte[(int)raf.length()];
raf.read(data);
//将字节数组转换成字符串
String str = new String(data,"UTF-8");
System.out.println(str);
raf.close();
*使用RAF写出字符串
* String提供了将字符串转换为字节的方法
* byte[] getBytes():按照系统默认字符集转换
*
* byte[] getBytes(String charset)
* 按照指定的字符集将字符串转换为字节,常见的
* 字符集:
* GBK:国标编码,英文1字节,中文2字节
* UTF-8:对unicode进行编码的字符集,也成为万国码
* 其中英文1字节,中文3字节,其他国家文字
* 占用不等的字节量
实例:
RandomAccessFile raf= new RandomAccessFile("raf.txt","rw");
String str = "我可以接受你的所有,所有小脾气.";
byte[] data = str.getBytes("UTF-8");
raf.write(data);
str = "我可以带你去吃很多,很多好东西.";
data = str.getBytes("UTF-8");
raf.write(data);
System.out.println("写出完毕!");
raf.close();
--------文件IO流操作API--------
文件流
FileInputStream
与FileOutputStream
* 文件流是一对低级流,是用来读写文件数据的流。
* 它们与RandomAccessFile都是用来读写文件数据的,但是
* 底层实际的读写方式不同。
* 文件流读取数据
实例:
FileInputStream fis= new FileInputStream("fos.txt");
byte[] data = new byte[100];
int len = fis.read(data);
String str = new String(data,0,len,"UTF-8");
System.out.println(str);
fis.close();
* 向文件中写入数据
* 文件流创建时,有两种写模式:
* 1:覆盖写模式,即如果该文件已经存在,则会先将
* 文件数据清除。然后通过该流写入的数据做为文件数据。
* 2:追加写模式,在构造方法中再传入一个boolean值
* 参数,若该值为true,则是追加模式。那么若文件
* 存在,则原数据保留,通过该流写入的数据会被追加到文件末尾。
实例:
// FileOutputStream fos= new FileOutputStream("fos.txt");
FileOutputStream fos= new FileOutputStream("fos.txt",true);
String str = "听我说,啊啊啊啊啊。";
byte[] data = str.getBytes("UTF-8");
fos.write(data);
System.out.println("写出完毕!");
fos.close();
BufferedOutputStream
*使用缓冲输出流写出字符串时的缓冲区问题
* void flush()
* 缓冲流的flush方法会强制将缓冲区中已经缓存的
* 数据一次性写出。
* 频繁调用flush会降低写效率,但是可以保证写出
* 数据的即时性。结合实际需求使用该方法。
实例:
FileOutputStream fos= new FileOutputStream("bos.txt");
BufferedOutputStream bos= new BufferedOutputStream(fos);
bos.write("丝瓜鞋,一起骂,噢!".getBytes("UTF-8"));
bos.flush();
System.out.println("写出完毕!");
/*
* 缓冲流的close方法中会自动调用一次flush
*/
bos.close();
//缓冲读写测试
public static void buffReadWrite() throws IOException {
//缓冲将数据写入文件
FileOutputStream fos=new FileOutputStream("fos.txt",true);
BufferedOutputStream bos=new BufferedOutputStream(fos);
bos.write("世界等一等,一曲相思!".getBytes("UTF-8"));
bos.flush();
System.out.println("写入成功!");
bos.close();
//缓冲读取文件数据
FileInputStream fis=new FileInputStream("fos.txt");
BufferedInputStream bis=new BufferedInputStream(fis);
byte[] data=new byte[1024];
while(bis.read(data) != -1) {
String str=new String(data,"UTF-8");
System.out.println(str);
}
bis.close();
}
BufferedInputStream
*使用缓冲流提高读写效率
* 缓冲流是一对高级流,使用它们读写数据时,无论我们的读写
* 方式是单字节读写还是块读写,它都能提高读写效率。
* 在流链接中搭配不同的高级流可以简化我们对数据的读写操作
* 所谓流链接:使用高级流链接其他流,最终链接在低级流上,
* 这样的串联操作可以对低级流上的数据做一系列的加工处理
* 工作。
实例:
FileInputStream fis= new FileInputStream("music.mp3");
//将缓冲输入流链接在文件输入流上
BufferedInputStream bis= new BufferedInputStream(fis);
FileOutputStream fos= new FileOutputStream("music_cp3.mp3");
BufferedOutputStream bos= new BufferedOutputStream(fos);
int d = -1;
/*
* 缓冲流实际将读写转换为了块读写操作,从而保证
* 读写的效率
*/
while((d = bis.read())!=-1) {
bos.write(d);
}
System.out.println("复制完毕!");
bis.close();
bos.close();
ObjectOutputStream
&& ObjectInputStream
* 对象流
* 对象流是一对常见的高级流,作用是可以方便我们读写任何java对象。
*
* 对象输出流:可以将java对象转换为一组字节后写出.
*
* 对象输入流:可以将一组字节读入并转换为对应的对象,
* 读取的字节必须是对象输出流将一个对象转换的字节
*
* 将一个Person类型的实例写入文件person.obj
实例:
String name = "苍老师";
int age = 18;
String gender = "女";
String[] otherInfo = {"是一名演员","爱好写毛笔字","促进中日文化交流","广大男性同胞的启蒙老师"};
Person p = new Person(name,age,gender,otherInfo);
System.out.println(p);
FileOutputStream fos= new FileOutputStream("person.obj");
ObjectOutputStream oos= new ObjectOutputStream(fos);
/*
* 对象输出流提供的独有方法:
* void writeObject(Object obj)
* 将给定对象转换为一组字节后写出。
*
* 需要注意,该方法要求写出的对象所属类必须
* 实现:Serializable接口,否则会抛出:
* NotSerializableException这个异常
*
* 首先Person对象经过oos的writeObject方法时
* 对象流按照Person的结构将其转换为了一组字节
* 而这个过程称为:对象序列化
*
* 然后这组字节再经过fos写入了文件中,那么将
* 数据写入文件(硬盘上)这个过程称为:数据持久化
*
*/
oos.writeObject(p);
System.out.println("写出完毕!");
oos.close();
* 对象输入流
实例:
//读取person.obj文件,将其中的对象读取回来
FileInputStream fis= new FileInputStream("person.obj");
ObjectInputStream ois= new ObjectInputStream(fis);
/*
* 对象输入流在读取了一组字节后按照其表示的结构
* 将其还原为对象的过程称为:对象的反序列化
*/
Person p = (Person)ois.readObject();
System.out.println(p);
ois.close();
* 使用该类测试对象流的对象读写操作
*
* 一个类的实例若希望被对象流读写,那么必须实现接口:
* java.io.Serializable
* 该接口不需要重写任何方法,但是实际上编译器在编译当前
* 类时若发现其实现了可序列化接口时,会隐含的添加一个方法
* 作用是将当前类实例转换为一组字节。而该方法不需要体现在
* 源代码中。
* 当一个类实现了序列化接口后,需要定义一个常量:
* serialVersionUID 序列化版本号
* 若不指定,则当前类在编译时,编译器会结合当前
* 源代码生成一个版本号,但这意味着当前类的源码
* 做出改动,版本号就会变化。
* 而版本号影响着反序列化的结果。
* 当对象输入流在读取一个对象进行反序列化时,会检查
* 该对象与当前类的版本号是否一致,一致则可以反序列化
* 但是不一致则会抛出异常。
private static final long serialVersionUID = 1L;
* 当一个属性被transient修饰后,在进行对象序列化时
* 该属性的值会被忽略。这意味着反序列化时得不到该
* 属性的值。
* 忽略不必要的属性,可以做到对象序列化时的"瘦身"效果
* 减少资源开销。
private transient String[] otherInfo;
BufferedReader
&& BufferedWriter
* 缓冲字符输入流
* java.io.BufferedReader
* 特点:可以按行读取字符串
实例:
* 将当前源代码读取出来并输出到控制台
/**流的三级关系**/
FileInputStream fis= new FileInputStream("src/io/BufferedReaderDemo.java");
InputStreamReader isr= new InputStreamReader(fis);
BufferedReader br= new BufferedReader(isr);
/*
* BR提供的方法:
* String readLine()
* 连续读取若干字符,直到读取了换行符为止,然后
* 将读取的字符以字符串形式返回。
* 注意,返回的字符串中不含有最后的换行符。
* 当返回值为null时,表示流读取到了末尾。
*/
String line = null;
while((line = br.readLine())!=null) {
System.out.println(line);
}
br.close();
/* 测试 */
public static void BufReadWrite() throws IOException {
//通过缓冲字符输入流读取文件类容
FileInputStream fis= new FileInputStream("pom.xml");
InputStreamReader isr=new InputStreamReader(fis,"UTF-8");
BufferedReader br=new BufferedReader(isr);
//通过缓冲字符输出流将数据写入文件
FileOutputStream fos=new FileOutputStream("bufWR.txt");
OutputStreamWriter osw=new OutputStreamWriter(fos, "UTF-8");
BufferedWriter bw=new BufferedWriter(osw);
String line =null;
while((line=br.readLine())!=null) {
bw.write(line);
}
System.out.println("读写成功!");
br.close();
bw.close();
}
Reader
&& Writer
* 字符流
* java按照流读写的数据单位划分为:字节流与字符流
* 由于读写文本数据是一件非常频繁的操作,对此java专门设计
* 了一套流,字符流。字符流是以字符为单位读写数据的,虽然
* 底层实际上还是读写字节(因为计算机只有二进制),但是字节
* 与字符之间的转换工作字符流自行完成了。对此也要注意到
* 字符流的局限性,仅适合读写文本数据。
*
* java.io.Reader和java.io.Writer
* 这两个类也是抽象类,它们分别是所有字符输入流与字符输出
* 流的超类,规定了字符流读写字符的相关方法。
InputStreamReader
&& OutputStreamWriter
* 转换流:
* java.io.InputStreamReader,java.io.OutputStreamWriter
* 它们是字符流的一对常用实现类,在使用字符流读写数据时,
* 作为流链接的重要一环存在。但实际应用时我们通常不会直接
* 操作这两个流(因为其上还会链接其他高级的字符流)。
实例:
/** 向文件中写入字符串*/
FileOutputStream fos= new FileOutputStream("osw.txt");
/*
* 在创建转换流时,第二个参数为指定的字符集,
* 这样通过转换流写出的文本数据都会以该字符集
* 转换为字节后写出。
*/
OutputStreamWriter osw= new OutputStreamWriter(fos,"UTF-8");
osw.write("在一瞬间有一百万种可能。");
osw.write("该向前走还是继续等。");
System.out.println("写出完毕!");
osw.close();
/* 测试 */
public static void ISRW() throws IOException {
//通过转换流将数据写入文件
FileOutputStream fos=new FileOutputStream("IOSrw.txt");
OutputStreamWriter osw=new OutputStreamWriter(fos,"UTF-8");
osw.write("在一瞬间有一百万种可能!");
osw.close();
//通过转换流读取文件数据
FileInputStream fis=new FileInputStream("IOSrw.txt");
InputStreamReader isr=new InputStreamReader(fis,"UTF-8");
char[] data=new char[1024];
while(isr.read(data)!=-1) {
System.out.println(data);
}
isr.close();
}
* InputStreamReader与OutputStreamWriter之所以称为
* "转换流",是因为其他的高级字符流都只能连接在其他的
* 字符流上,而不能直接连接在字节流上,而转换流是唯一
* 可以连接在字节流上的字符流,而它们本身是字符流,可以
* 供其他字符流连接,从而起到承上启下的作用。对此我们在
* 使用字符流操作时,它们在流链接中是用来衔接字节流与
* 字符流的桥梁。负责将字符与字节之间相互转换。
实例:
/** 从osw.txt文件中读取文本数据*/
FileInputStream fis= new FileInputStream("osw.txt");
InputStreamReader isr= new InputStreamReader(fis,"UTF-8");
int d = -1;
/*
* int read()
* 字符流定义的该方法是读取一个字符,实际从数据
* 源读取了几个字节取决于字符集,但是当读取到该
* 字符后在java内部都是以unicode编码保存,就变
* 为固定的2字节的char了。
*/
// while((d = isr.read())!=-1) {
// char c = (char)d;
// System.out.print(c);
// }
char[] data = new char[100];
int len = isr.read(data);
String str = new String(data,0,len);
System.out.println(str);
isr.close();
PrintWriter
* java.io.PrintWriter
* 具有自动行刷新的缓冲字符输出流
* 注:java.io.BufferedWriter是缓冲字符输出流,PW总是会
* 在内部链接它来完成缓冲工作,而PW在其基础上还支持自动的
* 行刷新操作,所以更为常用。
*
* 缓冲字符输出流除了写出字符效率高之外,还可以按行写出
* 字符串(写出个字符串后会自动添加换行符)。
实例:
* PW提供了直接写文件的构造方法
* PrintWriter(String path)
* PrintWriter(File file)
*
* 也支持重载构造方法,第二个参数为字符集,这样就
* 可以按照指定的字符集将字符写入文件。
*/
//向pw.txt文件中写字符串
PrintWriter pw = new PrintWriter("pw.txt","UTF-8");
pw.println("让我掉下眼泪的,不止昨夜的酒。");
pw.println("让我依依不舍的,不止你的温柔。");
System.out.println("写出完毕!");
pw.close();
//测试
public static void PWFileIO() throws IOException {
//通过自动行刷新字符缓冲流将数据写入文件
PrintWriter pw=new PrintWriter("pw.txt","UTF-8");
pw.println("让我掉下眼泪的,不止昨夜的酒。");
pw.println("让我掉下眼泪的,不止昨夜的酒。");
//不带自动行刷新
pw.print("让我掉下眼泪的,不止昨夜的酒。");
pw.flush();
pw.close();
System.out.println("写入成功!");
}
* 在流链接中使用PrintWriter
实例:/*逐级连接、四级连接*/
FileOutputStream fos= new FileOutputStream("pw.txt"); //字节流
OutputStreamWriter osw= new OutputStreamWriter(fos,"UTF-8");//转换流
BufferedWriter bw= new BufferedWriter(osw); //缓冲流
PrintWriter pw= new PrintWriter(bw); //行刷新输出流
pw.println("巴拉巴");
System.out.println("写出完毕!");
pw.close();
/* 内容写入文件 */
Date time=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
FileOutputStream fos= new FileOutputStream("D:/logs/YL_logs.txt",true);
PrintWriter pw= new PrintWriter(new BufferedWriter(new OutputStreamWriter(fos, "UTF-8")));
pw.println("时间:"+sdf.format(time));
pw.close();
* 完成简易记事本工具
* 输入文件名,对文件写操作,之后输入的每行字符串都
* 按行写入到文件中,当输入exit时程序退出。
*
* 注:创建PrintWriter使用流链接。
实例:
Scanner scanner = new Scanner(System.in);
System.out.println("请输入文件名:");
String fileName = scanner.nextLine();
FileOutputStream fos= new FileOutputStream(fileName);
OutputStreamWriter osw= new OutputStreamWriter(fos,"UTF-8");
BufferedWriter bw= new BufferedWriter(osw);
/*
* 在流链接中创建PrintWriter时,支持第二个参数
* 该参数为boolean类型,若为true,则表示创建的
* PrintWriter为自动行刷新模式。即:
* 每当调用println方法写出一行字符串后就会自动
* 进行flush操作。
* 注:调用print方法是不会flush的
*/
PrintWriter pw= new PrintWriter(bw,true);
System.out.println("请开始输入:");
while(true) {
String line = scanner.nextLine();
if("exit".equals(line)) {
break;
}
pw.println(line);
}
System.out.println("再见!");
pw.close();
try-catch
&& finally
* java异常处理机制中的try-catch
* 语法:
* try{
* 可能出现异常的代码片段
* }catch(XXXException e){
* 当try中出现XXXException后的解决办法
* }
实例:
System.out.println("程序开始了");
try {
String str = "a";
/*
* 当JVM执行到某一句话时若现了某种异常时,JVM
* 便会实例化对应异常的实例,并将整个代码的执行
* 过程设置进去,然后将该异常抛出来。
*/
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(StringIndexOutOfBoundsException e) {
System.out.println("字符串下标越界了!");
/*
* 应当养成一个好习惯,在最后一个catch中捕获
* Exception,这样可以避免因为一个未捕获的异常
* 导致程序中断。
*/
}catch(Exception e) {
System.out.println("反正就是出了个错!");
}
System.out.println("程序结束了");
* 自动关闭特性
* JDK7之后推出的,使得我们在IO操作中的异常处理写法更简洁。
实例:
/*
* 实现了AutoCloseable接口的类可以定义在这里。
* 所有的流和RandomAccessFile都实现
* 了该接口
* 编译器最终会将代码改为传统的try-catch-
* finally的形式,并在finally中关闭这里定义的流。
*/
try(FileOutputStream fos= new FileOutputStream("fos.dat",true))
{
fos.write(1);
} catch (Exception e) {
System.out.println("出错了!");
}
* finally块
* finally块是异常处理机制的最后一块。它可以直接跟在try块之后或者最后一个catch块之后。
* finally确保只要程序执行到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("程序结束了");
* try 后面可以连接多个catch
实例:
try {
System.out.println("test方法执行了...");
return str.charAt(0)-'0';
} catch (NullPointerException e) {
System.out.println("出现了空指针!");
return 1;
} catch (Exception e) {
System.out.println("出现了其他异常!");
return 2;
} finally {
System.out.println("执行了finally!");
return 3;
}
--------TCP网络通信编程--------
java.net.ServerSocket;
java.net.Socket;
创建服务端套接字:ServerSocket server=new ServerSocket(8088);
监听:Socket socket= server.accpet();
创建客户端套接字:Socket socket=new Socket("192.168.2.112",8088);
//网络TCP介绍另外开启一篇新文章
--------多线程编程--------
线程
- 线程可以并发执行多段代码,给我们感觉上好像这些代码在"同时运行"。
- 创建线程有两种方式:
方式一:继承Thread
并重写run
方法。
- 启动线程要调用start方法,而不是直接调用线程的run方法。
* 第一种创建线程的方式存在两个不足之处- 1:由于java是单继承的,这导致继承了Thread后就不能再继承其他类了。在实际开发中经常会继承某个超类来复用其中的方法,这导致两者不能同时继承。
- 2:继承线程后重写run方法来定义任务,这又导致我们将任务直接定义在线程上,使得线程只能做该任务,无法并发 执行其他任务,重用性变差。
方式一:Runnable
* 实现Runnable接口,单独定义线程任务
实例:
class MyRunnable1 implements Runnable{
public void run() {
for(int i=0;i<1000;i++) {
System.out.println("你是谁啊?");
}
}
}
/****启动方式****/
//实例化任务
Runnable r1 = new MyRunnable1();
//创建线程
Thread t1 = new Thread(r1);
t1.start();
//多线程测试
public static void main(String[] args) {
System.out.println("主线程执行开始!");
Thread th=new Thread() {
@Override
public void run() {
System.err.println("Thread 内部类实现多线程!");
}
};
Thread thr=new Thread(new Runnable() {
@Override
public void run() {
System.err.println("通过Runnable匿名内部类实现多线程!");
}
});
th.start();
thr.start();
System.out.println("主线程执行结束!");
}
线程提供了一系列获取当前线程信息的方法
实例:
//获取主线程
Thread main = Thread.currentThread();
//获取线程的唯一标识
long id = main.getId();
System.out.println("id:"+id);
//获取线程的名字
String name = main.getName();
System.out.println("name:"+name);
//获取优先级
int priority = main.getPriority();
System.out.println("priority:"+priority);
//显示是否处于活动状态
boolean isAlive = main.isAlive();
//显示是否为守护线程
boolean isDaemon = main.isDaemon();
//显示线程是否被中断
boolean isInterrupted = main.isInterrupted();
System.out.println("是否活着:"+isAlive);
System.out.println("是否为守护线程:"+isDaemon);
System.out.println("是否被中断:"+isInterrupted);
static Thread currentThread
* Thread提供了一个静态方法:
* static Thread currentThread()
* 该方法可以获取运行这个方法的线程。
* 后期常用的一个API: ThreadLocal里面就会用到这个方法来实现功能。
实例:
Thread main = Thread.currentThread();
System.out.println("运行main方法的线程是:"+main);
Thread t = new Thread() {
public void run() {
Thread t = Thread.currentThread();
System.out.println("自定义线程:"+t);
dosome(); //自定义方法
}
};
t.start();
线程优先级
* 线程无法主动获取CPU时间片,唯一可以干涉线程调度工作
* 的方式就是修改线程优先级,最大程度的改善获取CPU时间
* 片的几率。
* 理论上,线程优先级越高的线程获取CPU时间片的次数越多
* 线程有10个优先级,分别用整数1-10表示。
实例:
Thread max = new Thread() {
public void run() {
for(int i=0;i<10000;i++) {
System.out.println("max");
}
}
};
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");
}
}
};
max.setPriority(Thread.MAX_PRIORITY);
min.setPriority(Thread.MIN_PRIORITY);
min.start();
norm.start();
max.start();
join
方法可以协调线程之间的同步运行
* 异步运行:代码之间运行没有先后顺序,各干各的。
* 同步运行:代码运行有先后顺序。
实例:
//jdk1.7以下,局部内部类访问局部变量时,局部变量需要被final修饰
final 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() {
System.out.println("show:开始显示图片...");
/*
* 先等在下载线程将图片下载完毕
*/
try {
/*
* 当show线程调用download线程的join方法后就
* 进入了阻塞状态,直到download线程结束才会
* 解除阻塞。
*/
download.join();
} catch (InterruptedException e) {
}
if(!isFinish) {
throw new RuntimeException("图片加载失败!");
}
System.out.println("show:显示图片完毕!");
}
};
download.start();
show.start();
睡眠阻塞
* static void sleep(long ms)
* 该方法会让运行这个方法的线程处于阻塞状态指定的毫秒,
* 当超时后,线程会自动回到RUNNABLE状态,等待再次获取
* 时间片并发运行。
*
* 注:一个线程进入阻塞状态时,CPU会立刻释放去并发执行
* 其他线程,直到该线程解除阻塞状态为止。
实例:
/*
* 程序启动后,输入一个数字,如:100
* 然后每一秒钟递减一次并输出,到0时停止。
*/
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个数字:");
int num = Integer.parseInt(scanner.nextLine());
for(int i=num;i>0;i--) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("结束.");
多线程并发的安全问题
* 当多个线程并发操作同一资源时,由于线程切换时机的不确定
* 和不可控,会导致操作该资源的代码逻辑执行顺序未按照设计
* 要求运行,出现了操作混乱。严重时可能导致系统瘫痪。
* 解决并发安全问题的核心就是将多个线程抢着运行改为
* 有先后顺序的排队运行。
* Java提供了锁机制,强制多个线程同步运行一个方法
*
* 当一个方法上使用关键字:synchronized修饰后,该方法
* 称为同步方法,多个线程不能同时在方法内部运行。
实例:
public synchronized int getBean() {
if(beans==0) {
throw new RuntimeException("没有豆子了!");
/*
* yield方法会让运行这个方法的线程立刻让出CPU时间
* 在这里是为了模拟发生线程切换。
*/
Thread.yield();
return beans--;
}
* 同步块
* 有效的缩小同步范围可以在保证并发安全的前提下尽可能的提高并发执行效率。
*
* 语法:
* synchronized(同步监视器对象){
* 需用同步运行的代码片段
* }
实例:
class Shop{
/**
* 直接在方法上使用synchroinzed,那么同步监视器
* 对象就是当前方法所属对象,即:方法中看到的this
*/
//public synchronized void buy() {
public void buy() {
try {
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在挑衣服...");
Thread.sleep(5000);
/*
* 同步块有一个要求,多个线程看到的同步监视器
* 对象必须是同一个!否则没有同步运行效果。
* 具体使用哪个对象可以结合将来实际开发需求而定。
*/
synchronized (this) {
System.out.println(t.getName()+":正在试衣服...");
Thread.sleep(5000);
}
System.out.println(t.getName()+":结账离开");
} catch (Exception e) {
System.out.println("出现异常了");
}
}
}
* 静态方法若使用synchronized修饰后,那么一定具有同步
(静态方法属于类,只有一份,使用时必须一次等待)
public synchronized static void dosome() {
try {
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在运行dosome方法");
Thread.sleep(5000);
System.out.println(t.getName()+":运行dosome方法完毕");
} catch (Exception e) {
e.printStackTrace();
}
}
守护线程
* 守护线程又称为后台线程,默认创建出来的线程都是普通
* 线程或称为前台线程。只有调用线程的setDaemon方法后
* 才会将该线程设置为守护线程。
*
* 守护线程使用上与前台线程一样,但是在结束时机上有一点
* 不同:当进程结束时,所有正在运行的守护线程都会被强制
* 中断。
* 进程的结束:当一个进程中没有任何前台线程时即结束。
* 设置守护线程必须要在start方法之前进行。
jack.setDaemon(true);
jack.start();
--------线程池--------
* 线程池
* 线程池是一个管理线程的机制。它主要解决两个问题:
* 1:重用线程
* 2:控制线程数量
* 频繁的创建和销毁线程会给系统带来额外的开销,所以线程应当得以重用。
* 当线程数量过多时,会出现资源消耗增大,CPU出现过渡切换导致并发性能降低,
* 对此线程的数量也要得以控制在当前硬件环境所能承受的范围内。
实例:
public static void main(String[] args) {
/**
* 懒汉模式
*
* 饿汉模式
*/
ExecutorService threadPool=Executors.newFixedThreadPool(2);
for(int i=0;i<5;i++)
{
Runnable r=new Runnable(){
@Override
public void run() {
Thread t=Thread.currentThread();
System.out.println(t.getName()+":执行任务完毕!");
}
};
threadPool.execute(r);
System.out.println("将一个任务指派给了线程池"+i+"!");
}
threadPool.shutdown(); //线程池里面的线程完成任务之后结束线程
//threadPool.shutdownNow(); //线程马上结束
}
--------集合Collection(List & Set)--------
java.util.Collection
* 集合框架
* Collection接口是所有集合的*接口,规定了所有集合都应当具备的功能。
* 其下常见的分类:
* java.util.List:可重复集合,有序集
* java.util.Set:不可重复集合,无序
* 重复指的是元素是否重复,而标准是元素自身equals比较的结果。
实例:
Collection c = new ArrayList();
/*
* boolean add(E e)
* 向当前集合中添加给定元素,成功添加则返回true
*/
c.add("one");
c.add("two");
c.add("three");
c.add("four");
c.add("five");
System.out.println(c);
/*
* int size()
* 返回当前集合的元素个数
*/
int size = c.size();
System.out.println("size:"+size);
/*
* 判断集合是否为空集(不含有任何元素)
*/
boolean isEmpty = c.isEmpty();
System.out.println("isEmpty:"+isEmpty);
/*
* 清空集合元素
*/
c.clear();
System.out.println("集合已清空");
System.out.println(c);
System.out.println("size:"+c.size());
System.out.println("isEmpty:"+c.isEmpty());
* 集合存放的是元素的引用
Collection c = new ArrayList();
Point p = new Point(1,2); //Point表示类
* 集合操作
Collection c1 = new ArrayList();
c1.add("java");
c1.add("c");
c1.add("c++");
System.out.println("c1:"+c1);
Collection c2 = new HashSet();
c2.add("android");
c2.add("ios");
System.out.println("c2:"+c2);
/*
* 将给定集合中的所有元素添加到当前集合中
*/
c1.addAll(c2);
System.out.println("c1:"+c1);
Collection c3 = new ArrayList();
c3.add("java");
c3.add("ios");
c3.add("php");
/*
* 判断当前集合是否包含给定集合的所有元素
*/
boolean contains = c1.containsAll(c3);
System.out.println("c1全包含c3:"+contains);
/*
* 删除交集
* 从当前集合中删除与给定集合中的共有元素
*/
c1.removeAll(c3);
System.out.println("c1:"+c1);
* 泛型应用最广泛的地方就是集合,而集合中使用泛型是用来约束集合的元素类型
public static void main(String[] args) {
Collection<String> c = new ArrayList<String>();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
System.out.println(c);
for(String str : c) { //for each 新循环遍历数组
System.out.println(str);
}
/*
* 迭代器指定的泛型与其遍历的集合指定的泛型一致
* 即可
*/
Iterator<String> it = c.iterator();
while(it.hasNext()) {
String str = it.next();
System.out.println(str);
}
}
contains
* 集合可以判断是否包含给定元素,方法为:
* boolean contains(Object o)
* 当包含给定元素时返回true
public static void main(String[] args) {
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(9,0));
System.out.println(c);
Point p = new Point(1,2);
/*
* 集合的contains方法判断时是使用元素equals的
* 比较结果进行的。当给定元素与集合现有元素存在
* equals比较为true的情况时便返回true。
*/
boolean contains = c.contains(p); //false
System.out.println("包含:"+contains);
}
iterator
* 遍历集合元素
*
* Collection提供了一个方法:
* Iterator iterator()
* 该方法可以获取一个用于遍历当前集合元素的迭代器。
*
* java.util.Iterator接口
* 该接口是所有迭代器的*接口,规定了迭代器遍历集合的
* 统一操作。不同的集合实现类都实现了一个用于遍历自身元素
* 的迭代器实现类。我们无需记住它们,用接口接收它们并调用
* 相应遍历方法即可。
public static void main(String[] args) {
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);
/*
* 可以通过迭代器提供的remove方法删除
* 通过迭代器next出来的元素
*/
it.remove();
}
}
System.out.println(c);
}
for each
* JDK5推出时,推出了一个新的特性:
* 增强型for循环,也称为新循环,for each
*
* 新循环不取代传统循环的工作,只用来遍历集合或数组。
*
* 注:新循环的语法JVM是不认可的,而只是编译器认可,
* 编译器在编译源程序时会将新循环遍历改为传统方式遍历。
public static void main(String[] args) {
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);
}
System.out.println("=====================");
Collection c = new ArrayList();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
c.add("five");
/*
* 新循环遍历集合就是迭代器遍历,所以在遍历的
* 过程中不要通过集合的方法增删元素,否则会抛出
* 异常
*/
for(Object o : c) {
String str = (String)o;
//c.remove(p); 删除集合元素
System.out.println(str);
}
}
*删除集合元素
* remove方法删除元素时是删除与给定元素equals
* 比较为true的元素。注意:List集合而言,若存在
* 重复元素,只删除一个。
*/
c.remove(p);
泛型
* 泛型不指定则默认为原型Object
* 泛型是JDK5推出的一个特性,又称为参数化类型。允许使用
* 者在使用一个类的时候指定其定义的属性,方法的参数以及
* 返回值的类型。这样可以使得程序使用更灵活。
* 实际上编译后,Type类型名后面没有<T>.
* 而:
* private T x;
* 会被改为
* private Object x;
* 参数也会改为:
* public Type(Object x, Object y) {
*
* 下面的方法同理,返回值若是T,也会被改为Object。
泛型实例:
public static void main(String[] args) {
/*
* 使用时指定泛型的实际类型,需要注意,泛型的
* 实际类型必须是引用类型,不可以用基本类型。
*
* 泛型是编译器认可,而不是虚拟机认可。泛型的
* 原型就是Object。
* 也就是说,在编译Type这个类时,x,y的类型就是
* Object,所有泛型参数和返回值都会被改回Object
*
* 那么这里使用时,编译器也会将泛型删除,下面实例
* 化Type的过程会被编译器改为:
* Type t1 = new Type(1,2);
* 但是由于Type的构造方法要求参数符合泛型要求
* 所以编译器会在编译时检查实际参数是否符合要求
* 不符合时编译不通过。
* 由于构造方法中的实参1,2都是整数,符合这里t1
* 指定的泛型Integer那么编译通过。
*/
Type<Integer> t1 = new Type<Integer>(1,2);
//编译不通过,实参不符合泛型要求
//ype<Integer> t1 = new Type<Integer>("1","2");
t1.setX(2);
/*
* 当获取以泛型定义的返回值类型数据时,编译器会
* 自动补充造型代码,将返回值造型为泛型所指定的
* 实际类型。
* 下面的代码会被编译器改为:
* int x1 = (Integer)t1.getX();
* 当然这里还会触发自动拆箱,改为:
* int x1 = ((Integer)t1.getX()).intValue();
*/
int x1 = t1.getX();
System.out.println("x1:"+x1);
System.out.println("t1:"+t1);
Type<Double> t2 = new Type<Double>(1.1,2.2);
double x2 = t2.getX();
System.out.println("x2:"+x2);
System.out.println("t2:"+t2);
Type<String> t3 = new Type<String>("一","二");
String x3 = t3.getX();
System.out.println("x3:"+x3);
System.out.println("t3:"+t3);
}
toArray
* Collection中提供了一个方法:toArray
* 可以将当前集合转换为一个数组。
实例:
Collection<String> c = new ArrayList<String>();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
System.out.println(c);
//Object[] array = c.toArray();
String[] array = c.toArray(new String[c.size()]);
System.out.println(array.length);
System.out.println(Arrays.toString(array));
java.util.List
* java.util.List
* List接口是Collection的一个子接口,称为线性表。
*
* List集合的特点:可以重复,并且有序。提供了一组通过下标操作元素的方法。
* 常见实现类:
* java.util.ArrayList:数组实现,查询性能更好
* java.util.LinkedList:链表实现,增删元素性能更好,尤其首尾增删元素效果最优。
实例:
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
System.out.println(list);
/*
* E get(int index)
* 获取给定下标对应的元素
*/
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 element)
* 将给定元素设置到指定位置,返回值为原位置
* 对应的元素(被替换的元素)
* [one,two,three,four,five,six]
* [one,2,three,four,five]
*/
String old = list.set(1, "2");
System.out.println(list);
System.out.println("old:"+old);
List —> add && remove
* List提供了一对重载的add,remove方法。都是支持通过
* 下标操作对应元素
实例:
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
System.out.println(list);
/*
* void add(int index,E e)
* 在指定位置插入给定元素
*/
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:"+old);
List —> subList && asList
* List提供获取子集的操作:
*
* List subList(int start,int end)
* 获取指定范围内的子集
实例:
List<Integer> list = new ArrayList<Integer>();
for(int i=0;i<10;i++) {
list.add(i);
}
System.out.println(list);
//获取3-7
List<Integer> subList = list.subList(3, 8);
System.out.println(subList);
/*
* 将子集元素各扩大10倍
* [30,40,50,60,70]
*/
for(int i=0;i<subList.size();i++) {
int num = subList.get(i);
num = num*10;
subList.set(i, num);
}
System.out.println(subList);
/*
* 对子集元素操作就是对原集合对应元素的操作,
* 集合中存放的是引用类型,即:存放的地址;
* 所以操作子集也就是操作原来的集合
*/
System.out.println(list);
/*
* 将list集合中2-8删除
*/
list.subList(2, 9).clear();
System.out.println(list);
* 数组转换为集合
* 数组的工具类Arrays提供了一个静态方法:asList
* 可以将一个数组转换为一个List集合。
实例:
public static void main(String[] args) {
String[] array = {"one","two","three","four"};
System.out.println("Array:"+Arrays.toString(array));
/*
* 通过Arrays的asList将数组转换的集合表示的就是
* 该数组,所以对集合元素操作就是对该数组元素操作
*/
List<String> list = Arrays.asList(array);
System.out.println("list:"+list);
list.set(1, "2");
System.out.println("list:"+list);
System.out.println("Array:"+Arrays.toString(array));
/*
* 由于数组是定长的,所以该集合不可以增删元素。
*/
// list.add("five");
/*
* 若希望对集合增删元素,需要自行创建一个集合并导入
* 该集合元素
*/
//List<String> list2 = new ArrayList<String>();
//list2.addAll(list);
/*
* 所有的集合都支持一个参数为Collection的构造方法,
* 作用是在创建当前集合的同时包含给定集合中的所有元素
*/
List<String> list2 = new ArrayList<String>(list);
System.out.println("list2:"+list2);
list2.add("five");
System.out.println("list2:"+list2);
}
synchronizedList
&& synchronizedSet
* Collections提供了将现有的集合转换为并发安全的集合
实例:
public static void main(String[] args) {
/*
* 常见的集合:
* ArrayList,LinkedList,HashSet
* 它们都不是线程安全的,并发操作存在安全问题。
*/
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
/*
* 将给定List集合转换为并发安全的List集合
*/
list = Collections.synchronizedList(list);
System.out.println(list);
Set<String> set = new HashSet<String>(list);
set = Collections.synchronizedSet(set);
System.out.println(set);
/*
* API文档有说明这个事情,一个并发安全的集合不和
* 迭代器遍历做互斥,这意味着并发同时做上述两件事
* 会出现并发安全问题。
* 所以当使用迭代器遍历时要自行维护与集合的互斥关系
* 参考聊天室中服务端的处理
*/
}
栈
* 栈也可以保存一组元素,存取必须遵循先进后出原则
*
* 使用栈一般是为了实现如"后退"这样的功能
实例:
public static void main(String[] args) {
Deque<String> stack = new LinkedList<String>();
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);
}
集合的排序
*排序字符串
实例:
List<String> list = new ArrayList<String>();
list.add("tom");
list.add("jerry");
list.add("JACK");
list.add("TOMAS");
list.add("MIKE");
list.add("bill");
list.add("jackson");
list.add("michell");
System.out.println(list);
/*
* 字符串比较大小的规则是按照首字符的unicode编码
* 比较,若第一个字符一样则看第二个,以此类推。
*/
Collections.sort(list);
System.out.println(list);
* 集合的排序
* 集合有一个工具类:java.util.Collections
* 它提供了很多便于我们操作集合的静态方法,其中有一个
* 方法是:sort,用于对List集合进行自然排序。
实例:
public static void main(String[] args) {
Random random = new Random();
List<Integer> list = new ArrayList<Integer>();
for(int i=0;i<10;i++) {
list.add(random.nextInt(100));
}
System.out.println(list);
Collections.sort(list);
System.out.println(list);
}
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("苍老师");
list.add("传奇");
list.add("小泽老师");
System.out.println(list);
Collections.sort(list);
System.out.println(list);
sortString(list);
List<Point> list2 = new ArrayList<Point>();
list2.add(new Point(2,3));
list2.add(new Point(8,9));
list2.add(new Point(4,6));
list2.add(new Point(1,1));
list2.add(new Point(5,0));
System.out.println(list2);
/*
* 编译不通过。因为Collections的这个sort方法
* 要求集合元素必须实现Comparable接口。java提供
* 的常用数据类型,比如包装类,字符串等都实现了
* 这个接口并定义了比较规则,所以可以使用。但是
* 我们定义的类Point并没有实现该接口。
*
*
* 当我们定义的类,如:Point,它并没有实现Comparable
* 接口,若我们想使用该sort方法排序,就需要实现这个
* 接口,并重写其中的比较方法。但是这样做是不推荐的。
* 因为这样做具有侵入性,写程序时应当尽量避免侵入性
* 这样利于程序的扩展和重构。
*
* 所谓侵入性指的是:
* 当我们使用某个功能方法时,除了调用该功能方法的语句
* 之外,该功能方法要求我们为其额外的添加其他的代码
* 比如这里要求Point类实现接口并重写方法,那么这时
* 该功能就对我们的程序具有侵入性。
*/
// Collections.sort(list2);
sortPoint(list2);
}
/**
* Collections的重载sort方法,由于可以传入一个额外
* 的比较规则,所以这个sort方法不强制要求元素必须
* 实现Comparable接口。
* @param list
*/
public static void sortPoint(List<Point> list) {
Collections.sort(list,new Comparator<Point>() {
public int compare(Point o1, Point o2) {
int len1 = o1.getX()*o1.getX()+o1.getY()*o1.getY();
int len2 = o2.getX()*o2.getX()+o2.getY()*o2.getY();
return len1-len2;
}
});
System.out.println("排序Point后的结果:"+list);
}
/**
* 利用Collections重载的sort方法,额外的传入一个
* 比较规则(Comparator比较器实现类)来对已经实现了
* Comparable接口并定义了比较规则的元素按照我们定义
* 的比较规则重新排序。
* @param list
*/
public static void sortString(List<String> list) {
/*
* 定义额外比较规则
* 为字符串提供一种比较规则:字符少的小,字符多的大
*/
Collections.sort(list,new Comparator<String>() {
/**
* 该方法是用来定义o1与o2比较大小的规则。
* 而返回值是一个int值,该值不关注具体取值,只
* 关注取值范围:
* 当返回值>0:表示o1大于o2
* 当返回值<0:表示o1小于o2
* 当返回值=0:两个对象相等
*/
//重写 int compare(T o1, T o2);
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
});
System.out.println("自定义排序效果:"+list);
}
Queue
* java.util.Queue接口
* Queue是队列,其继承自Collection。
* 队列可以保存一组元素,但是存取元素必须遵循先进先出原则
*
* 常用实现类:java.util.LinkedList
实例:
public static void main(String[] args) {
Queue<String> queue = new LinkedList<String>();
/*
* offer方法,入队操作,将给定元素添加到队列末尾
*/
queue.offer("one");
queue.offer("two");
queue.offer("three");
queue.offer("four");
System.out.println(queue);
/*
* poll方法:出队操作
* 获取并从队列中删除队首元素
*/
String str = queue.poll();
System.out.println(str);
System.out.println(queue);
/*
* peek:引用队首元素
* 与poll的区别在于获取后并不会将其从队列中删除
*/
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 s = queue.poll();
System.out.println(s);
}
System.out.println(queue);
}
Deque
* 双端队列 java.util.Deque
* 它继承自Queue,是两端都可以进出队的队列。
*
* 常用实现类:java.util.LinkedList
public static void main(String[] args) {
Deque<String> deque = new LinkedList<String>();
deque.offer("one");
deque.offer("two");
deque.offer("three");
System.out.println(deque);
deque.offerFirst("four");
System.out.println(deque);
deque.offerLast("five");
System.out.println(deque);
String str = deque.poll();
System.out.println(str);
System.out.println(deque);
str = deque.pollLast();
System.out.println(str);
System.out.println(deque);
str = deque.pollFirst();
System.out.println(str);
System.out.println(deque);
}
BlockingQueue
* 阻塞队列
* 阻塞队列是并发安全的队列,采用双缓冲,在并发安全的前提下解决存取互斥问题,并发效率更好
实例:
public static void main(String[] args) {
BlockingQueue<String> queue = new ArrayBlockingQueue<String>(3);
boolean b = queue.offer("one");
System.out.println(b);
queue.offer("two");
queue.offer("three");
try {
b = queue.offer("four", 5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(b);
System.out.println(queue);
}
Map
* java.util.Map 查找表
* Map体现的结构是一个多行两列的表格
* 左列名为key,右列名为value
* value是保存要查找的值,而对应的key是查询条件。
*
* 在一个Map中key是不允许重复的。
*
* 常用实现类:java.util.HashMap
* 也称为散列表或哈希表
* 当今世界上查询速度最快的数据结构。
实例:
public static void main(String[] args) {
Map<String,Integer> map= new HashMap<String,Integer>();
/*
* V put(K k,V v)
* 将给定的Key-Value对保存到Map中
* 由于Map要求key不允许重复,那么当使用一个
* 已有的key保存新的value时,那么是做替换value
* 操作,这时put方法会将被替换的value返回。否则
* 返回值为null
* 注:若value是包装类类型时,接收put方法返回值
* 时要使用包装类类型而不要用基本类型,避免因为
* 自动拆箱导致空指针出现
*
*/
Integer num = map.put("语文", 98);
System.out.println(num);
map.put("数学", 97);
map.put("英语", 96);
map.put("物理", 95);
map.put("化学", 98);
System.out.println(map);
//替换value操作
num = map.put("语文", 88);
System.out.println(num);
System.out.println(map);
/*
* 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所对应的键值对删除,删除后会将
* 该key对应的value返回。
*/
num = map.remove("数学");
System.out.println(map);
System.out.println(num);
int size = map.size();
System.out.println("size:"+size);
/*
* 判断是否包含指定的key和value
*/
boolean ck = map.containsKey("语文");
System.out.println("包含key:"+ck);
boolean cv = map.containsValue(96);
System.out.println("包含value:"+cv);
}
Map的遍历
* Map提供了三种遍历方式:
* 1:遍历所有的key
* 2:遍历所有的键值对(Entry)
* 3:遍历所有的value(相对不常用)
实例:
public static void main(String[] args) {
Map<String,Integer> map= new HashMap<String,Integer>();
map.put("语文", 98);
map.put("数学", 97);
map.put("英语", 96);
map.put("物理", 95);
map.put("化学", 98);
System.out.println(map);
/*
* 遍历所有的key
* Set<K> keySet()
* 将当前Map中所有的key以一个Set集合形式返回
* 遍历该集合等同于遍历了所有的key
*/
Set<String> keySet = map.keySet();
for(String key : keySet) {
System.out.println("key:"+key);
}
/*
* 遍历每一组键值对
* Set<Entry> entrySet()
*
* java.util.Map.Entry
* Entry的每一个实例用于表示当前Map的一组键值对
* 其有两个常用方法
* getKey,getValue
*/
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<Integer> values = map.values();
for(Integer value : values) {
System.out.println("value:"+value);
}
}
--------反射机制--------
* java的反射机制
*
* 反射允许们在实例化一个类,操作其属性和方法从编码期决定转为在运行期决定。
* 这样做可以提高代码的灵活性,适度使用,过度使用会降低代码的运行效率,增加资源开销。
/**
* Class类
* Class类的每个实例用于表示jvm加载的一个类。并且在jvm内部每个被加载的类有且只有一个Class实例与之对应。
*
* 获取一个类的类对象有以下几种方式:
* 1:直接调用该类的静态属性class,
* 例如: Class cls=String.class
* 但是这样做是在编码期间确定某个类的类对象,相对不灵活。
*
* 2:调用Class的静态方法forName
* 例如:
* Class cls=Class.forName("java.lang.String");
*
* 3:通过类加载器ClassLoad
* 例如:
*
*/
实例:
public static void main(String[] args) {
/**
* 加载Person类
* Class.forName中指定的参数为要加载的类的完全限定,即:包名.类名
*/
try {
/**
* 若指定的类完全限定有误时,会抛出异常:ClassNoteFoundException
*/
Class<?> cls=Class.forName("reflect.Person");
String name=cls.getName();
System.out.println(name);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
* 通过类对象,可以获取其表示的类的一切信息:类名、属性、方法、构造器 等等。
* 并且可以通过调用无参构造器快速实例化一个对象。
实例:
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
try {
Class<?> cls=Class.forName("reflect.Person");
/**
*
* 获取当前类定义的所有构造器
* getConstructors()
*/
Constructor<?>[] arr=cls.getConstructors();
for(Constructor<?> cos:arr)
{
System.out.println(cos);
}
/**
* 获取当前类所有的方法有两个方式:
*
* 获取当前类自定义的所有方法;
* getDeclaredMethods()
*
* 获取当前类的所有方法(包括从超类继承过来的方法)
* getMethods()
*/
Method[] arr1=cls.getDeclaredMethods();
for(Method met:arr1)
{
System.out.println(met);
}
Person p=new Person();
System.out.println(p);
/**
* Class提供了一个方法:newInstance
* 可以调用其表示的类的无参构造方法进行实例化,并将实例化的对象返回。
*/
Object obj=cls.newInstance();
System.out.println(obj);
//强制转换之后在调用自己的方法。
Person ob=(Person)obj;
ob.sayHi();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
* 通过反射机制调用无参方法
public static void main(String[] args) throws Exception{
/**
* 利用反射机制
*/
//1、加载类对象
Class cls=Class.forName("reflect.Person");
//2、通过类对象实例化
Object obj=cls.newInstance();
//3、获取无参的sayHi方法
//第二个参数为参数类型
Method method=cls.getDeclaredMethod("sayHi",null);
//如果该方法是私有的,则可以调用setAccessible修改方法的访问权限
//method.setAccessible(true);
//4、调用该方法
method.invoke(obj,null);
/**
* Method 类的每个实例用于表示一个类中的一个方法,其提供了动态调用该方法的操作:
* Object invoke(Object o,Object[] arg) //Object invoke(Object obj, Object... args)
* 第一个参数为调用哪个实例的对应方法,第二个参数为调用该对应的方法时传递的实际参数。
*/
}
* 调用有参数方法
public static void main(String[] args) {
try {
/**
* 利用反射机制
*/
//1、加载类对象
Class<?> cls=Class.forName("reflect.Person");
//2、通过类对象实例化
Object obj=cls.newInstance();
//3、获取无参的sayHi方法(传入参数类型,参数列表必须一一对应)
Method method=cls.getDeclaredMethod("sayHello",String.class,int.class); //传入对应的参数类型
//4、调用该方法
method.invoke(obj, "是这么一回事儿!",12);
} catch (Exception e) {
e.printStackTrace();
}
}
反射综合案例:
public static void main(String[] args) throws ClassNotFoundException,IllegalArgumentException,Exception {
Scanner scan=new Scanner(System.in);
System.out.println("请输入反射类全名:");
String name=scan.nextLine();
//利用java反射加载类,并且将类实例化
Class<?> cls=Class.forName(name);
Object obj=cls.newInstance();
//获取该类的所有方法
Method[] methods=cls.getDeclaredMethods();
for(Method me:methods){
System.out.println(me.getName());
//获得方法的参数类型
Class[] types=me.getParameterTypes();
//如果types的长度为0,该方法不带参数
Object returnVal=null; //Object invoke()返回值类型为Object
if(types.length==0){
//调用无参方法
returnVal=me.invoke(obj);
}else{
//创建一个Object数组用来存放参数值
Object[] params=new Object[types.length];
for(int i=0;i<types.length;i++){
//判断参数的类型,此处需要改进,不可能把所有类型写完。***
if(types[i]==String.class){
params[i]="HelloWorld!";
}
if(types[i]==int.class){
params[i]=100;
}
}
//调用带有参数的方法
returnVal=me.invoke(obj,params);
}
System.out.println("returnVal:"+returnVal);
}
scan.close();
}
--------日期类--------
Date
* java.util.Date
* 日期类,其中每一个实例用于表示一个切确的时间点。
* Date内部维护了一个long值,该值为自公元1970-01-01 00:00:00 到其表示的时间之间所经过的毫秒。
* 由于Date存在失去和千年虫的问题,大部分操作时间的方法在jdk1.1版本出现时就变为过时方法了,不在建议使用。
实例:
public static void main(String[] args) {
/**
* Date默认创建出来表示当前系统时间
*/
Date date=new Date();
System.out.println(date);
/**
* getTime();方法可以获取Date内部维护的long值
*/
long mill=date.getTime();
System.out.println(mill);
/**
* setTime(long date);方法可以设置一个long值(毫秒),使其表示对应的日期
*/
date.setTime(0);
//Thu Jan 01 08:00:01 CST 1970 按照时间格式输出
String time=date.toString();
System.out.println(time);
}
* java.util.SimpleDateFormat
public static void main(String[] args) {
/**
* 2018-10-25 10:22:35
* yyyy-MM-dd HH:mm:ss
*/
Date now=new Date();
System.out.println(now);
String str="yyyy-MM-dd HH:mm:ss";
SimpleDateFormat sdf=new SimpleDateFormat(str);
/**
* String format(Date date)
* 将给定的Date 按照当前SDF指定的日期格式转换为字符串
*/
String line=sdf.format(now);
System.out.println(line);
}
* 将一个字符串解析为Date
public static void main(String[] args) throws ParseException {
String str="2008-08-08 20:08:08";
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date=sdf.parse(str);
System.out.println(date);
}
练习实例:
* 程序启动后,要求用户输入自己的生日,格式:yyyy-MM-dd
* 然后经过程序计算,输出到今天为止一共活了多少天?
* 以及出生10000天的纪念日是哪天,纪念日格式同上
public static void main(String[] args) throws ParseException {
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);
}
Calendar
* java.util.Calendar;
* 日历类,用于操作时间的API。
* Calendar本身是一个抽象类。
* 常见实现类:GregoranCalendar, 即:阳历
*
* 可以通过Calendar提供的静态方法:getInstance方法获取一个当前地区时间的实现类,
* 绝大数地区获取的都是阳历。
实例:
public static void main(String[] args) {
/**
* 默认创建也是表示当前系统时间
*/
Calendar calendar=Calendar.getInstance();
System.out.println(calendar);
/**
* Calendar提供了与Date之间相互转换的方法: Date getTime()
* 将当前Calendar表示的日期以一个Date实例形式返回。
*
* void setTime(Date date)
* 使当前Calendar表示给定的Date所表示的是日期
*/
Date date=calendar.getTime();
System.out.println(date);
}
Calendar提供了获取指定时间分量对应值的操作
* int get(int filed)
* 参数为对应的时间分量,返回值为该时间分量所对应的值。
* 而时间分量Calendar提供了大量的常量来表示。
实例:
public static void main(String[] args) {
Calendar cal=Calendar.getInstance();
/**
* 获取年
*/
int year=cal.get(Calendar.YEAR);
/**
* 获取月
*/
int month=cal.get(Calendar.MONTH)+1;
/**
* 获取日
*/
//int day=cal.get(Calendar.DAY_OF_MONTH);
int day=cal.get(Calendar.DATE);
System.out.println(year+"-"+month+"-"+day);
//获取时分秒
int h=cal.get(Calendar.HOUR_OF_DAY);
int m=cal.get(Calendar.MINUTE);
int s=cal.get(Calendar.SECOND);
System.out.println(h+":"+m+":"+s);
//今天是今年的第几天
int days=cal.get(Calendar.DAY_OF_YEAR);
System.out.println(days);
//今天是周几?
int dayw=cal.get(Calendar.DAY_OF_WEEK);
String[] data={"日","一","二","三","四","五","六"};
System.out.println(data[dayw]);
//今年一共多少天
int countDay=cal.getActualMaximum(Calendar.DAY_OF_YEAR);
System.out.println("今年一共:"+countDay+"天!");
//当月一共多少天
int conday=cal.getActualMaximum(Calendar.DAY_OF_MONTH);
System.out.println("当月一共:"+conday+"天!");
}
* void set(int field,int value)
* 设置指定时间分量为给定的值
public static void main(String[] args) {
Calendar calen=Calendar.getInstance();
System.out.println(calen.getTime());
//设置年份为2008
calen.set(Calendar.YEAR, 2008);
System.out.println(calen.getTime());
//设置月份 例如:8 月
//calen.set(Calendar.MONTH,7);
calen.set(Calendar.MONTH,Calendar.AUGUST);
System.out.println(calen.getTime());
//设置号数为 8号
calen.set(Calendar.DAY_OF_MONTH, 8);
System.out.println(calen.getTime());
//设置小时
calen.set(Calendar.HOUR, 9);
System.out.println(calen.getTime());
//设置分钟
calen.set(Calendar.MINUTE, 55);
System.out.println(calen.getTime());
//设置秒数
calen.set(Calendar.SECOND,20);
System.out.println(calen.getTime());
}
* void add(int field,int amount)
* 对指定的时间分量累加指定的值,若给定的值为负数,则是减去
public static void main(String[] args) {
Calendar calen=Calendar.getInstance();
//calen.add(Calendar.MONTH, 1);
System.out.println(calen.getTime());
/**
* 查看 3 年 2 个月 25 天以后的那周的周五是哪天?
*/
calen.add(Calendar.YEAR, 3);
calen.add(Calendar.MONTH, 2);
calen.add(Calendar.DAY_OF_MONTH, 25);
System.out.println(calen.getTime());
calen.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
System.out.println(calen.getTime());
}
*案例:
* 计算商品的促销日期,
* 输入一个商品的生产日期,格式:yyyy-MM-dd
* 再输入该商品的的保质期天数。
* 然后经过程序计算输出该商品的促销日期,格式同上
*
* 促销日期计算规则:该商品过期日期前两周的周三
public static void main(String[] args) throws ParseException {
Scanner scan=new Scanner(System.in);
System.out.println("请输入商品的生产日期(格式:yyyy-MM-dd):");
String product=scan.nextLine();
System.out.println("请再次输入商品的保质期:");
int day=Integer.parseInt(scan.nextLine());
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
Calendar calen=Calendar.getInstance();
Date date=sdf.parse(product); //date为输入的生产日期
calen.setTime(date);
calen.add(Calendar.DAY_OF_MONTH, day);
calen.add(Calendar.WEEK_OF_MONTH, -2);
//calen.add(Calendar.DAY_OF_YEAR, -14);
calen.set(Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY);
//String line=sdf.parse(date);
System.out.println(calen.getTime());
}
--------lambda新特性 --------
* lambda表达式
* lambda是JDK1.8推出的一个特性,让我们可以 以函数式编程
*
* lambda可以更简单的完成匿名内部类的创建。
* 语法规则:
* ([参数列表])->{
* 方法体
* }
* 使用lambda创建的匿名内部类所属结构必须只能有1个抽象方法,否则无法使用。
Runnable r2=()->{
System.out.println("2:HelloWorld!");
};
/**
* 如果方法中只有一句代码,那么lambda中的"{}"可以省略。
*/
Runnable r3=()->System.out.println("3:HelloWorld!");
/**
* 如果方法只有一句代码,那么在删除"{}"的同时,若方法要求返回值时,"return"关键字也必须省略
*/
Collections.sort(list,(o1,o2)-> o1.length()-o2.length());
* JDK1.8之后遍历集合,Map推出了一个方法:forEach
* 该方法可以使用lambda来遍历集合。
* 由于该方法是集合自身的方法进行遍历,那么如果该集合是一个
* 并发安全的集合时,这种遍历方法是可以和其他集合操作进行互斥的。达到并发安全。
*
* 原迭代器方式是不与集合自身方法互斥的,需要自行维护并发安全问题。
public static void main(String[] args) {
List<String> list=new ArrayList<String>();
list.add("苍老师");
list.add("饭桶");
list.add("苍山有井");
list.add("苍山有井,水为空");
System.out.println(list);
list.forEach((e)->System.out.println(e));
list.forEach(str->System.out.println(str));
Map<String, Integer> map=new HashMap<String, Integer>();
map.put("语文", 98);
map.put("数学", 97);
map.put("英语", 96);
map.put("物理", 95);
map.put("化学", 94);
//forEach遍历Map集合时需要传入两个参数
map.forEach((k,v)->System.out.println(k+":"+v));
}
--------java注解 --------
注解类:ATest.jsva
/**
* Retention是一个元注解,用来告诉JVM注解保留到什么时候。
* 所谓元注解,指的是有系统自带,用来解释其他注解的注解。
* 注解默认只保留到字节码文件里面,一旦运行,注解会被抹掉;
* 将注解的保留时间设置为RUNTIME,表示该注解在运行时任然存在。
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface ATest {
/**
* 注解可以带有属性,如果属性名为value,则在使用注解时
* 可以不必指定属性名。比如:@ATest("World")
*/
public String value();
}
测试类:B.java
public class B {
@ATest(value = "Hello")
public void m1(){
System.out.println("B's m1()...");
}
//没有注解
public void m2(){
System.out.println("B's m2()...");
}
//value 属性名可以省略,默认就是value
@ATest("World")
public void hello(){
System.out.println("B's hello()...");
}
}
测试注解读取属性类:TestCase
public static void main(String[] args) throws Exception{
//反射
Class<?> cls=Class.forName("reflect.B");
Object obj=cls.newInstance();
Method[] methods=cls.getDeclaredMethods();
for(Method mh:methods){
//获得添加在方法前边的注解
ATest test=mh.getDeclaredAnnotation(ATest.class);
System.out.println(test);
//只执行带有@ATest注解的方法
if(test!=null){
mh.invoke(obj);
//读取@ATest注解的属性值
String v1=test.value();
System.out.println("value():"+v1);
}
}
}
--------解析XML配置文件--------
使用DOM4J
解析XML
文档
*案例:
public class ParseXmlDemo {
public static void main(String[] args) {
/*
* 将emplist.xml文件中的所有员工信息解析出来并
* 存入集合
*/
List<Emp> list = new ArrayList<Emp>();
/*
* 解析流程:
* 1:创建SAXReader
* 2:使用SAXReader读取xml文档并生成Document对象
* 这一步就是DOM解析耗时耗资源的地方,因为会将
* 当前XML文档解析为一棵树的结构并存入Document
* 对象,那么内容越多内存消耗越多,并且要求读取
* 完整个XML文档所以相对耗时。
* 3:通过Document对象获取根元素
* 4:通过根元素开始逐级获取子元素,以达到遍历XML
* 文档数据的目的
*/
try {
//1
SAXReader reader = new SAXReader();
//2
Document doc = reader.read(new File("emplist.xml"));
//reader.read(new FileInputStream("emplist.xml"));
/*
* 3
* Document提供了获取根元素的方法:
* Element getRootElement()
*
* Element的每一个实例用于表示XML文档中的一个元素
* 通过Element可以获取其表示的元素的相关信息
*
* String getName()
* 获取当前元素的名字
*
* String getText()
* 获取当前元素中间的文本(开始与结束标签中间的文本)
*
* Element element(String name)
* 获取当前标签中指定名字的子标签
*
* List elements()
* 获取当前标签中所有子标签
*
* List elements(String name)
* 获取当前标签中所有指定名字的子标签
*
*/
Element root = doc.getRootElement();
System.out.println("root:"+root.getName());
/*
* 获取根标签下所有的<emp>标签
*/
List<Element> empList = root.elements("emp");
System.out.println("<emp>标签个数:"+empList.size());
/*
* 遍历集合,通过每个<emp>标签获取到对应员工信息
*/
for(Element empEle : empList) {
//获取员工名字
//1获取<name>标签
Element nameEle = empEle.element("name");
//2获取<name>标签中间的文本
String name = nameEle.getText();
System.out.println(name);
//获取年龄
int age = Integer.parseInt(empEle.element("age").getText());
/*
* Element提供了简便的获取指定名字子标签中间
* 文本的方法:
* String elementText(String name)
* 例如:
* empEle.elementText("gender")
* 它等同于
* empEle.element("gender").getText()
*/
//获取性别
String gender = empEle.elementText("gender");
//获取工资
int salary = Integer.parseInt(empEle.elementText("salary"));
//获取属性:id
/* Attribute attr = empEle.attribute("id");
int id = Integer.parseInt(attr.getValue());
int id = Integer.parseInt(empEle.attribute("id").getValue());
*/
int id = Integer.parseInt(empEle.attributeValue("id"));
Emp emp = new Emp(id, name, age, gender, salary);
//将元素添加进入集合
list.add(emp);
}
System.out.println("解析完毕!");
for(Emp emp : list) {
System.out.println(emp);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用DOM4J
生成一个XML
文档
*案例:
public class WriteXmlDemo {
public static void main(String[] args) {
List<Emp> list = new ArrayList<Emp>();
list.add(new Emp(1, "张三", 22, "男", 5000));
list.add(new Emp(2, "李四", 23, "女", 6000));
list.add(new Emp(3, "王五", 24, "男", 7000));
list.add(new Emp(4, "赵六", 25, "女", 8000));
list.add(new Emp(5, "钱七", 26, "男", 9000));
/*
* 生成一个XML文档的大致步骤
* 1:创建一个Document对象,表示一个空白文档
* 2:向Document中添加根元素
* 3:向根元素中逐级添加子元素已达到XML文档应有的结构
* 4:创建XmlWriter
* 5:将Document对象通过XmlWriter写出以生成XML文档
*/
try {
//1
Document doc = DocumentHelper.createDocument();
/*
* 2
* Document提供了添加根元素的方法:
* Element addElement(String name)
* 添加指定名字的根元素,并以Element实例形式
* 将其返回,以便于继续对该根元素操作。
* 注意,这个方法只能调用一次!
*/
Element root = doc.addElement("list");
/*
* 将每个员工信息以一个<emp>标签添加到<list>中
*/
for(Emp emp : list) {
//向<list>中添加子标签<emp>
Element empEle = root.addElement("emp");
//添加员工姓名
Element nameEle = empEle.addElement("name");
nameEle.addText(emp.getName());
Element ageEle = empEle.addElement("age");
ageEle.addText(emp.getAge()+"");
Element genderEle = empEle.addElement("gender");
genderEle.addText(emp.getGender());
Element salaryEle = empEle.addElement("salary");
salaryEle.addText(emp.getSalary()+"");
empEle.addAttribute("id", emp.getId()+"");
}
XMLWriter writer = new XMLWriter(
new FileOutputStream("myemp.xml"),
OutputFormat.createPrettyPrint()
);
writer.write(doc);
System.out.println("写出完毕!");
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
解析XML
文件,结合反射
*案例:
public void init() throws ServletException {
//用于存放处理器实例
List<Object> beans=new ArrayList<Object>();
/**
* 读取SmartMVC框架的配置文件(smartmvc.xml),
* 利用反射机制获取处理器的类,
* 然后将处理器实例化。
*/
SAXReader reader=new SAXReader();
//获取文件输入流
InputStream in = getClass().getClassLoader().getResourceAsStream("smartmvc.xml");
try {
//读取配置文件的内容,dom4j会根据xml文件的内容,构造一颗对应的树。
Document doc=reader.read(in);
//找出根节点
Element root=doc.getRootElement();
//找出根节点的所有子节点
List<Element> elements=root.elements();
//遍历所有子节点
for(Element ele:elements){
//获得子节点的属性值
String className=ele.attributeValue("class");
//System.out.println("处理器类名:"+className);
Object bean=Class.forName(className).newInstance();
beans.add(bean);
}
System.out.println(beans);
} catch (Exception e) {
e.printStackTrace();
throw new ServletException(e);
}
}
使用Jsoup
解析XML
文档
jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。
它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。
1、导包
2、获取Document对象
3、获取对应的标签Element对象
4、获取数据
"快速入门"
public static void main(String[] args) throws IOException {
//1导包
//2获取Document对象,根据xml文档获取
//2.1获取student.xml的path
String path=JsoupXML.class.getClassLoader().getResource("student.xml").getPath();
//2.2解析xml文档,加载文档进内存,获取dom树
Document doc=Jsoup.parse(new File(path), "UTF-8");
//3 获取元素对象,Element
Elements eles=doc.getElementsByTag("name");
Elements eles2=doc.getElementsByTag("student");
System.out.println(eles.size());
//3.1获取第一个name的Element对象
Element ele=eles.get(0);
String text=ele.text(); //获取文本值
Element ele2=eles2.get(0);
String attr=ele2.attr("number"); //获取属性值
System.out.println("name:"+text);
}
对象的使用
1、Jsoup:工具类,可以解析html或xml文档,返回Document
*parse :解析html或xml文档,返回Document对象。
Jsoup.parse(File in,String charsetName):解析xml或html文件
Jsoup.parse(String html):解析xml或html字符串
Jsoup.parse(url, timeoutMillis)
URL url=new URL("https://www.baidu.com/");
Document doc=Jsoup.parse(url, 10000);
System.out.println(doc);
2、Document:文档对象。代表内存中的dom树
*获取Element对象
Element getElementById(String id):根据id属性值来获取唯一的Element对象。
Elements getElementsByTag(String tagName) :根据标签名称获取元素对象集合
Elements getElementsByAttribute(String key):根据属性名称获取元素对象集合
Elements getElementsByAttributeValue(String key, String value)
根据对应的属性名和属性值获取元素对象集合
3、Elements:元素Element对象的集合。可以当做ArrayList<Element>来使用
4、Element:元素对象,对应的标签对象
1、获取子元素对象
Element getElementById(String id):根据id属性值来获取唯一的Element对象。
Elements getElementsByTag(String tagName) :根据标签名称获取元素对象集合
Elements getElementsByAttribute(String key):根据属性名称获取元素对象集合
Elements getElementsByAttributeValue(String key, String value)
根据对应的属性名和属性值获取元素对象集合
2、获取属性值
String attr(String attributeKey):根据属性名称获取属性的值
3、获取文本内容
String text():获取所有子标签的纯文本内容
String html():获取子标签体的标签和内容
5、Node:节点对象
*是Document和Element的父类。
快捷查询方式
1、selector:选择器
*使用方法:Elements select(String cssQuery)
Elements eles=doc.select("name"); //获取所有name标签
System.out.println(eles);
System.out.println("1-----------------------------");
Elements ele1=doc.select("#itcast"); //获取所有id值为itcast的标签
System.out.println(ele1);
System.out.println("2-----------------------------");
//获取student 并且number="001"的标签的子标签 age
Elements ele2=doc.select("student[number='001'] age");
System.out.println(ele2);
2、XPath:
XPath即为XML路径语言(XML Path Language),它是一种用来确定XML文档中某部分位置的语言。
*使用Jsoup的XPath需要额外导入jar包
如果你能看里,说明已经看完这篇技术文档,祝你:基础扎实、技术飞升、工资翻倍!别忘了点个赞,支持一下 。
本文地址:https://blog.csdn.net/Challenge_and_smiles/article/details/107084920
上一篇: 微信提现API实现(企业付款到个人)
下一篇: springboot 原理(如何启动)