# api
# 文档注释
package doc;
/** 已读
* 文档注释
* 文档注释是功能级注释,用于说明一个类,方法,常量的功能。
* 在类上使用文档注释,用于说明设计此类的意图,以及这个类解决的问题等信息。
*
* @author chengyi
* @version 1.0
* @see java.lang.String
* @since JDK1.0
*/
public class DocDemo {
/**
* sayHello方法中的问候语
*/
public static final String INFO = "你好!";
/**
* 对给定的用户添加问候语
* @param name 给定的用户名
*/
public void sayHello(String name){
System.out.println(INFO+name);
}
}
# String
# String内存管理
package string;
/** 已读
* 字符串
* @author chengyi
*
*/
public class StringDemo {
public static void main(String[] args){
String s1 = "abc"; //直接量才会指向同一地址,new出的对象肯定不是一
String s2 = "abc"; 个地址
System.out.println(s1==s2);//true
String s3 = s1;
System.out.println(s3==s2);//true
s1 += "!";
System.out.println(s1);
System.out.println(s1==s2);//false
//new 一定创建新对象
String s4 = new String("abc");
System.out.println(s4==s2);//false
/**
* 编译器有一个优化措施:
* 当一个计算表达式计算符号两边都是字面量时,编译器会直接将结果计算出来并
* 替代原来的表达式,所以JVM在运行class文件时,看到的下面代码被编译器改成为了:
* String s5 = "abc";
* 所以会指向上面s2的对象
*/
String s5 = "a"+"bc";
System.out.println(s5==s2);//true
//改变内容会创建新对象
String s6 = "a";
String s7 = s6+"bc";
System.out.println(s7==s2); //false
System.out.println(s7.equals(s2)); //true
}
}
# indexOf
package string;
/** 已读
* int indexOf(String str)
* 查看给定字符串在当前字符串中的位置,若当前字符串
* 不含有该内容则返回值为-1
* @author chengyi
*
*/
public class IndexDemo {
public static void main(String[] args) {
// 0123456789012345
String str = "thinking in java";
int index = str.indexOf("in");
System.out.println(index);//2
//从指定位置开始找
index = str.indexOf("in", 3);
System.out.println(index);//5
//查找最后一次出现给定字符串的位置
index =str.lastIndexOf("in");
System.out.println(index);//9
}
}
# Substring
package string;
/**
* String substring(int s, int e)
* 截取当前字符串中指定范围内的字符串
* @author chengyi
*
*/
public class SubstringDemo {
public static void main(String[] args) {
// 0123456789012345
String str = "thinking in java";
//截取:ing
String sub = str.substring(5, 8);//含头不含尾
System.out.println(sub);// ing
sub = str.substring(9);
System.out.println(sub);//in java
}
}
# Trim
package string;
/**
* String trim()
* 去除当前字符串两边的空白字符
* @author chengyi
*
*/
public class TrimDemo {
public static void main(String[] args) {
String str = " hello ";//前面是三空格,后面两个缩进
System.out.println(str);
String trim = str.trim();
System.out.println(trim);
}
}
# charAt
package string;
/**
* char charAT(int index)
* 获取指定位置对应的字符
* @author chengyi
*
*/
public class CharAtDemo {
public static void main (String[] args){
// 0123456789012345
String str = "thinging in java";
//查看第5个字符是什么?
char c = str.charAt(4);//输入的是下标
System.out.println(c);//g
/*
* 回文
* 上海自来水来自海上
* 我爱罗爱我
*/
String info = "上海自来水来自海上";
for(int i=0; i<info.length()/2; i++){
if( info.charAt(i)!=info.charAt(info.length()-1-i)){
System.out.println("不是回文");
/*当方法的返回值类型为void时,
* return是可以单独使用的,作用是
* 结束方法。
*/
return;
}
}
System.out.println("是回文");
}
}
# startsWith和endsWith
package string;
/**
* boolean startsWith(String str)
* boolean endsWith(String str)
* 判断当前字符串是否以给定的字符开始或结束
* @author chengyi
*
*/
public class StartWithDemo {
public static void main(String[] args) {
String str = "thinking in java";
boolean starts = str.startsWith("thi");
System.out.println("starts:"+starts);
boolean ends = str.endsWith("av");
System.out.println("starts:"+ends);
}
}
# toUpperCase和toLowerCase
package string;
/**
* String toUpperCase()
* String toLowerCase()
* 将当前字符串中的英文部分转换为全大写或全小写
* @author chengyi
*
*/
public class ToUpperCaseDemo {
public static void main (String[] args){
String str = "我爱Java";
String upper = str.toUpperCase();
System.out.println(upper);
String lower = str.toLowerCase();
System.out.println(lower);
}
}
# valueOf
package string;
/**
* String 提供了一组重载的静态方法:valuOf
* 作用是将Java中其他类型转换为字符串。
* 常见的是将基本类型转换为字符串
* @author chengyi
*/
public class ValueOfDemo {
public static void main(String[] args){
int b = 100;
String str = String.valueOf(b);
System.out.println("str:"+str); //100
int d = 10;
str = d+"";
System.out.println("str:"+str); //10
}
}
# StringBuilder
TIP
包含 append() insert() delete() replace() reverse()这里没有代码
- java的字符串连接的过程就是利用StringBuilder实现的
int d = 10;
String str = d+"";
String str1 = new StringBuilder(“”).append(10).toString();
- StringBuffer和StringBuilder
- StringBuffer是线程安全,同步处理的,性能稍慢
- StringBuilder是非线程安全的,并发处理的,性能稍快
package string;
/**
* StringBuilder是专门设计用来修改字符串内容的。
* 内部维护一个可变的字符数组,开销小,性能好。
* 所以凡是有频繁修改字符串内容操作时,都应用它来完成
* @author chengyi
*
*/
public class StringBuilderDemo {
public static void main(String[] args){
/*
* 默认创建的StringBuilder表示一个空字符串
* String str = "";
*/
//StringBuilder builder = new StringBuilder();
StringBuilder builder = new StringBuilder("好好学习Java");
String str = builder.toString();
System.out.println(str); //好好学习Java
/*
* append方法用于追加给定字符串
* 好好学习Java,为了找个好工作!
*/
builder.append(",为了找个好工作!");
str = builder.toString();
System.out.println(str);//好好学习Java,为了找个好工作!
/*
* 好好学习Java,就是为了改变世界!
*
* replace 替换部分内容
*/
builder.replace(9, 16, "就是为了改变世界");
str = builder.toString();
System.out.println(str); //好好学习Java,就是为了改变世界!
/*
* 好好学习Java,就是为了改变世界!
* ,就是为了改变世界!
*
*delete删除指定范围内的字符串
*/
builder.delete(0, 8);
str = builder.toString();
System.out.println(str);//,就是为了改变世界!
/*
* ,就是为了改变世界!
* 活着,就是为了改变世界!
*
* insert将指定内容插入到指定位置
*/
builder.insert(0, "活着");
str = builder.toString();
System.out.println(str);//活着,就是为了改变世界!
}
}
# 正则表达式
# matches
方法:将一个字符串正则表达式进行匹配,如果匹配成功返 回true,否则返回false。
package string;
/**
* 字符串支持正则表达式的方法之一:
* boolean matches(String regex)
* 使用给定的正则表达式验证当前字符串是否满足格式要求,
* 满足则返回true
* @author chengyi
*
*/
public class MatchesDemo {
public static void main(String[] args) {
String mail = "fancq@tedu.cn";
/*
* emil对应的正则表达式
* [\w]+@[a-zA-Z0-9]+(\.[a-zA-Z]+)+
*/
String regex = "[\\w]+@[a-zA-Z0-9]+(\\.[a-zA-Z]+)+";
boolean flag = mail.matches(regex);
if(flag){
System.out.println("地址有效");
}else{
System.out.println("地址无效");
}
}
}
```
### Split
String Split(String regex) 将字符串按照特定的分隔符拆分成字符串数组
```java
package string;
/**
* String 支持正则表达式方法二:
* String[] split(String regex)
* 将当前字符串按照满足正则表达式的部分进行拆分
* 返回所有被拆分后的部分
* @author chengyi
*
*/
public class SplitDemo {
public static void main(String[] args) {
//
String str = "1111abc123def456ghi1111";
/*
* 按照数字部分进行拆分
* abc def ghi
*/
/*
* 若拆分是连续匹配了拆分内容,那么会拆分出空字符串。但是若在字符串末尾连续
* 匹配了拆分内容,那么所有拆分的空字符串都被忽略
*/
//String[] array = str.split("[0-9]+");//4
String[] array = str.split("[\\d]");
System.out.println(array.length);//11
for(int i=0; i<array.length;i++){
System.out.println(array[i]);
}
}
}
```
### replaceAll
replaceAll(String regex, String replacement)<br>
将字符串中匹配正则表达式regex的字符串替换成replacement
```java
package string;
/**
* 字符串支持正则表达式方法三:
* String replaceAll(String regex, String str)
* 将当前字符串中满足正则表达式的部分替换为给定的字符串。
* @author chengyi
*
*/
public class ReplaceAllDemo {
public static void main(String[] args) {
String str = "abc123def456ghi";
/*
* 将数字部分替换为#num#
* abc123def456ghi
* 替换后 abc#num#def#num#ghi
*
*/
str = str.replaceAll("[\\d]+", "#num#");
System.out.println(str);
str = "abc123def456ghi";
str = str.replaceAll("[a-z]+", "#CHAR#");
System.out.println(str);
}
}
2.Object i.在Java类继承顶端中,Java.lang.Object类位于顶端; ii.如果定义一个Java类时没有extends关键字声明其父类,则其父类默认为java.lang.Object类; iii.Object类型的引用变量可以指向任何类型对象 a)重写toString b)重写equals c)equals和==的区别 i.==用于比较变量的值,可以应用于任何类型,如果用于引用类型,比较的是两个引用变量中存储的值(地址信息),判断两个变量是否指向相同的对象; ii.equals是Object的方法,重写以后,可以比较两个对象的内容是否“相等”; iii.需要注意的是,Object默认的equals方法的比较规则同==; package object; /** * 使用该类测试重写object相关方法的原则 * 该类表示直角坐标系中放入一个点 * @author chengyi / public class Point { private int x; private int y; //直接右键source里面添加的 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+")"; } /* * equals的设计目的是比较两个对象内容是否相同 * 这里指的是当前对象“this“和参数对象obj的内容是否相同 / 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; } } package object; /* * 测试Point重写的Object方法 * @author chengyi * / public class TestPoint { public static void main(String[] args) { Point p = new Point(1,2); / * Object定义的toString方法返回的是该对象的地址信息 * 实际开发中意义不大。所以我们要使用,通常会重写这个方法。 * 注意:java提供给我们的类妥善重写过该方法。只有我们自己 * 定义的类通常要重写。 / String str = p.toString(); System.out.println(str); / * System.out.println(Object o) * 该方法会将给定对象toString方法返回的字符串输出到控制台 / System.out.println(p); Point p2 = new Point(1,2); System.out.println(p==p2); / * Object提供的equals内部就是"=="比较 * 所以不重写没有实际意义。 */ System.out.println(p.equals(p2)); } }
## 包装类
### xxvalue
```java
package integer;
/**
* 由于基本类型不能直接参与面向对
象的开发,所以java提供了8个基本类型所对应的包装类,
* 从而乐意将基本类型转换为“对象”,这样就可以参与面向对象的模式进行开发了。
*
* 6个数字类型的包装继承自java.lang.Number
* Number是一个抽象类,定义了可以让数字类型之间进行互装的方法。
* @author chengyi
*
*/
public class IntegerDemo1 {
public static void main(String[] args) {
double d = 128;
//Integer i1 = new Integer(d);
//Integer i2 = new Integer(d);
Double i1 = Double.valueOf(d);
Double i2 = Double.valueOf(d);
System.out.println(i1==i2); //flase,对比的是地址
System.out.println(i1.equals(i2)); //true对比的是内容
//包装类转换为基本类型
int in = i1.intValue();
System.out.println(in); //128
double dou = i1.doubleValue();
System.out.println(dou); //128.0
byte by = i1.byteValue();
System.out.println(by); //-128
}
}
# parseXXX
package integer;
/**
* 包装类支持一个静态方法:parseXXX(String str)
* 可以将给定的字符串解析为对应的基本类型,但是前提是该字符串内容必
* 须正确表示基本类型可以保存的值。
* @author chengyi
*
*/
public class integerDemo3 {
public static void main(String[] args) {
String str = "123";
int d = Integer.parseInt(str); // 如果str是小数就不行,不能够正确表示
System.out.println(d);
double dou = Double.parseDouble(str);
System.out.println(dou);
}
}
# MAX_VALUE,MIN_VALUE
package integer;
/**
* 数字类型的包装类都有两个常量:MAX_VALUE,MIN_VALUE
* 用于表示其对应的基本类型数据的取值范围
* @author chengyi
*
*/
public class IntegerDemo {
public static void main(String[] args) {
int max = Integer.MAX_VALUE;
System.out.println(max);
int min = Integer.MIN_VALUE;
System.out.println(min);
long lmax = Long.MAX_VALUE;
System.out.println(lmax);
char cmax = Character.MIN_VALUE;
System.out.println(cmax);
}
}
# autoPackage
package integer;
/**
* JDK1.5之后推出了一个特性:自动拆装箱
* 该特性是编译器认可,而非JVM认可。编译器在编译源代码时会自动补全基本类型与包装类之间的转换
* 代码完成相互转换工作,从而让我们在编写源码时实现了基本类型与包装类之间相互赋值的操作。
* @author chengyi
*
*/
public class IntegerDemo4 {
public static void main(String[] args) {
/*
* 触发了编译器自动拆箱特性
* 编译器会自动补全代码完成包装类转换为基本类型的操作。
* 下面代码在class文件中会被改为:
* int d = new Integer(1).intValue();
*/
int d = new Integer(1);
/*
* 触发了编译器自动装箱特性
* 编译器会自动补全代码完成基本类型转换为包装类的操作,
* 下面代码在class文件中会被改为:
* Integer in = Integer.valueOf(1);
*/
Integer in = 1;
}
}
```
## file
### file
```java
package file;
import java.io.File;
/**
* java.io.File
* File的每一个实例可以用于表示文件系统中的一个文件或目录
* 使用File可以:
* 1:访问文件或目录的属性(名字,大小,修改时间等);
* 2:操作文件或目录(创建, 删除)
* 3:访问一个目录的子项
* 但是不能读写文件数据。
* @author chengyi
*
*/
public class FileDemo {
public static void main(String[] args) {
/*
* 路径尽量选取相对路径,不同系统的路径方式不同,相对路径
* 可以做到夸平台。
* eclipse中当前目录(./)是当前程序所在项目的目录
* 例如
* windows:D:/workspace/JSD1803SE/
* mac:/Users/chengyi/Documents/workspace/JSD1803SE/demo.txt
*/
File file = new File("./demo.txt");
//获取名字
String name = file.getName();
System.out.println("名字:"+name);
//获取大小(字节数)
long len = file.length();
System.out.println("长度:"+len);
//可读可写
boolean cr = file.canRead();
boolean cw = file.canWrite();
System.out.println("可读性:"+cr);
System.out.println("可写性:"+cw);
//是否隐藏
boolean ih = file.isHidden();
System.out.println("是否隐藏:"+ih);
}
}
# file
# createNewFile
package file;
import java.io.File;
import java.io.IOException;
/**
* 使用File创建一个文件
* @author chengyi
*
*/
public class CreateNewFileDemo {
public static void main(String[] args) throws IOException {
/*
* 在当前目录下创建test.txt文件
*/
File file = new File("./test.txt");
/*
* boolean exists()
* 判断当前File表示的文件或目录是否已经存在
*/
if(!file.exists()){
file.createNewFile();
System.out.println("创建完毕!");
}else{
System.out.println("文件已存在");
}
}
}
# delete
package file;
import java.io.File;
/**
* 删除一个文件
* @author chengyi
*
*/
public class DeleteFileDemo {
public static void main(String[] args) {
/*
* 将当前目录中的test.txt文件删除
*/
File file = new File("./test.txt");
if(file.exists()){
file.delete();
System.out.println("删除完毕");
}else{
System.out.println("该文件不存在");
}
}
}
# mkDir
package file;
import java.io.File;
/**
* 使用File新建一个目录
* @author chengyi
*
*/
public class MkDirDemo {
public static void main(String[] args) {
/*
* 在当前目录下新建一个目录demo
*/
File dir = new File("./demo");
if(!dir.exists()){
dir.mkdir();
System.out.println("目录已创建");
}else{
System.out.println("目录已存在");
}
}
}
# mkdirs
package file;
import java.io.File;
/**
* 创建一个多级目录 MkDir s
* @author chengyi
*
*/
public class MkDirsDemo {
public static void main(String[] args) {
/*
* 在当前目录下创建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
package file;
import java.io.File;
public class DeletDirDemo {
public static void main(String[] args) {
/*
* 将当前目录中的demo目录删除
*/
File dir = new File("./demo");
if(dir.exists()){
/*
* 删除目录的前提条件是该目录是一个空目录
*/
dir.delete();
System.out.println("删除完毕");
}else{
System.out.println("目录不存在!");
}
}
}
# listFile
TIP
本方法的重写方法比较重要
package file;
import java.io.File;
/**
* 获取一个目录中的子项
* @author chengyi
*
*/
public class ListFilesDemo {
public static void main(String[] args) {
/*
* 获取当前目录(项目目录)中的所有子项
*/
File dir = new File(".");
/*
* boolean isFile()
* Boolean isDirectory()
* 判断当前File表示的是文件还是目录
*/
if(dir.isDirectory()){
//获取所有子项
File[] subs = dir.listFiles();
for(int i=0; i<subs.length; i++){
System.out.println(subs[i].getName());
}
}
}
}
# listFiles
package file;
import java.io.File;
import java.io.FileFilter;
/**
* File提供了一个重载的listFiles方法,可以指定一个文件过滤器,
* 然后将一个目录中满足过滤器要求的子项返回。不满足则忽略
* @author chengyi
*
*/
public class ListFilesDemo2 {
public static void main(String[] args) {
/*
* 获取当前目录中的文本文件?
*/
File file = new File(".");
//File[] subs = file.listFiles(new MyFilter());
//利用匿名内部类
File[] subs = file.listFiles(new FileFilter(){
public boolean accept(File file){
return file.getName().endsWith(".txt");
}
}
);
System.out.println("len:"+subs.length);
for(int i=0; i<subs.length; i++){
System.out.println(subs[i].getName());
}
}
}
class MyFilter implements FileFilter{
public boolean accept(File file){
String name = file.getName();
System.out.println("正在过滤:"+name);
return name.endsWith(".txt");
// return file.isFile(); //判断文件
}
}
```
### RandomAccessFile
```java
package raf;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* java.io.RandomAccessFile
* 该类设计用来专门读写文件数据。其基于指针进行读写,
* 即:总是在指针当前位置读或写字节。
*
* RAF有两种常用创建模式:
* "r":只读模式
* "rw":读写模式
* @author chengyi
*
*/
public class RandomAccessFileDemo {
public static void main(String[] args) throws IOException {
/*
* RAF常用构造方法:
* RandomAccessFile(String path, String mode)
* RandomAccessFile(File file, String mode)
* mode:操作模式,只读或读写
*/
RandomAccessFile raf = new RandomAccessFile("./raf.dat","rw");
/* 如果没有这个文件,会直接创建一个
* void write(int d)
* 写出一个字节,将给定的int值对应的2进制的“低八位”写如文件
* vvvvvvvv
* 00000000 00000000 00000000 00000001
*/
raf.write(255);//每次都是从头写,所以每次运行总是覆盖第一个字节
raf.write(256);
System.out.println("写出完毕");
raf.close();
}
}
package raf;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* 读取一个字节
* @author chengyi
*
*/
public class ReadDemo {
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile("./raf.dat","r");
/*
* int read()
* 读取1个字节,并以int形式返回。
* 若返回值为-1.则表示读取到了文件末尾。
*/
int d = raf.read();
System.out.println(d); //255
d = raf.read();
System.out.println(d); //0
raf.close();
}
}
# copyFile
package raf;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* 复制文件
* @author chengyi
*
*/
public class CopyDemo {
public static void main(String[] args) throws IOException {
/*
* 复制当前目录中的img.png
*/
RandomAccessFile src = new RandomAccessFile("./img.png","r");
RandomAccessFile srcs = new RandomAccessFile("./img_cc.png","rw");
int d ;//记录每次读取的字节数据
long star = System.currentTimeMillis();
while((d = src.read())!=-1){
srcs.write(d);
}
long end = System.currentTimeMillis();
System.out.println("完毕,耗时:"+(end-star)+"ms");
src.close();
srcs.close();
}
}
# read&write
package raf;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* 提高每次读写的数据量,减少实际读写的次数,可以提高读写的效率
* 对于硬盘(磁盘)而言,随机读写效率是缺点,但是硬盘
* 块读写效率晒是可以保证的。
* 随机读写:单字节读写
* 块读写:一组一组字节读写
* @author chengyi
*/
public class CopyDemo2 {
public static void main(String[] args) throws IOException {
RandomAccessFile src = new RandomAccessFile("./DG.mp4","r");
RandomAccessFile desc = new RandomAccessFile("./DG_cp.mp4","rw");
/**
* int read(byte[] data)
* 一次性读取给定的字节数组长度的字节量并存入到该数组中,返回值为实际读取
* 到的字节量。若返回值为-1,则是文件末尾
*
* void write(byte[] data)
* 一次性将给定的字节数组中所有字节写出
*
* void write(byte[] data,int index, int len)
* 将给定的字节数组从下标index处开始的连续len
* 个字节一次性写出
*
* 1byte = 8位2进制
* 1kb = 1024 byte
* 1mb = 1024 kb
* 1gb = 1024mb
* 1tb = 1024gb
*/
//10kb
byte[] buf = new byte[1024*10];
int len = -1;
long star = System.currentTimeMillis();
while((len=src.read(buf))!=-1){
desc.write(buf,0,len);
}
long end = System.currentTimeMillis();
System.out.println("复制完成,耗时:"+(end-star));
src.close();
desc.close();
}
}
# 读取字符串
String重载的自动将给定的字符集还原为字符串
package raf;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* 读取字符串
* @author chengyi
*
*/
public class ReadStringDemo {
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile("raf.txt","rw");
byte[] by = new byte[(int)raf.length()];//返回的就是byte[]
raf.read(by); //将得到的值就直接赋值给by
/*
* String提供了一组重载的构造方法
* 可以将给定的字节数组按照指定字符集还原为字符串
*/
String str = new String(by,"GBK");
System.out.println(str);
raf.close();
}
}
# getByte
package raf;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* 写出字符串操作
* @author chengyi
*
*/
public class WriteStringDemo {
public static void main(String[] args) throws IOException {
/*
* 在相对路径中"./"可以不写,不写默认也是当前目录中
*/
RandomAccessFile raf = new RandomAccessFile("raf.txt","rw");
/*
* String 提供的方法:
* byte[] getBytes()
* 将当前字符串按照系统默认字符集转换成为一组字节。
*
* byte[] getByte(String csn);
* 将当前字符串按照指定字符集转换为一组字节。
* 推荐使用这种方式转换字符串
* 因为按照系统默认字符集转换会导致跨平台出现乱码问题
*
* GBK:国标编码,中文占2字节
* UTF-8:万国码,对unicode进行编码,变长编码集。英文1字节,中文3字节
* IOS8859-1:欧洲编码集,不支持中文。
*/
String str = "程意无敌!";
byte[] bts = str.getBytes("GBK");
raf.write(bts);//将得到的int值直接赋值给bts
raf.write("程意是天才的克星!".getBytes("GBK"));
System.out.println("写出完毕");
raf.close();
}
}
# readLong
package raf;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* 读写基本类型数据,以及RAF指针的操作
* @author chengyi
*
*/
public class RanDomAccessFileDemo2 {
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile("dat.dat","rw");
long pos = raf.getFilePointer();
System.out.println("指针位置:"+pos);
//向文件中写入int最大值
int max = Integer.MAX_VALUE;
/*
* vvvvvvvv
* 01111111 11111111 11111111 11111111
* max>>>24
* 00000000 00000000 00000000 01111111
*/
raf.write(max>>>24);
raf.write(max>>>16);
raf.write(max>>>8);
raf.write(max);
System.out.println("指针位置:"+raf.getFilePointer());
/*
* RAF提供了方便我们写出基本类型的相关方法
*/
//一次将给定的int值的4字节全部写出
raf.writeInt(Integer.MIN_VALUE);
pos = raf.getFilePointer();
System.out.println("指针位置:"+pos);
raf.writeLong(123L);
pos = raf.getFilePointer();
System.out.println("指针位置:"+pos);
raf.writeDouble(123.123);
System.out.println("指针位置:"+raf.getFilePointer());
raf.seek(0);
System.out.println("指针位置:"+raf.getFilePointer());
//读取 EOF是end of file缩写
int d = raf.readInt();
System.out.println(d);
System.out.println("指针位置:"+raf.getFilePointer());
//读取long
raf.seek(8);
long l = raf.readLong();
System.out.println(l);
System.out.println("指针位置:"+raf.getFilePointer());
double dou = raf.readDouble();
System.out.println(dou);
System.out.println("指针位置:"+raf.getFilePointer());
System.out.println("写入完毕");
raf.close();
}
}
# 注册程序
package raf;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
import java.util.Scanner;
/**
* 完成用户注册业务操作
* 用户信息包含:用户名,密码,昵称,年龄
* 其中年龄是int值,其他三项为字符串
*
* 每个用户的信息都要写入文件user.dat中保存。
* 每条记录的长度都固定为100字节,其中
* 用户名,密码,昵称三项各占32字节,年龄int值固定为4字节。
*
* 字符串“留白”操作便于修改内容。不会影响整个文件的格式。
* @author chengyi
*/
public class Demo1 {
public static void main(String[] args) throws IOException {
Scanner scan = new Scanner(System.in);
System.out.println("欢迎注册!");
System.out.println("请输入你的名字");
String name = scan.nextLine();
System.out.println("请输入你的密码:");
String password = scan.nextLine();
System.out.println("请输入你的昵称:");
String cik = scan.nextLine();
System.out.println("请输入你的年龄");
int age = Integer.parseInt(scan.nextLine());
System.out.println("你的名字:"+name);
System.out.println("你的密码:"+password);
System.out.println("你的昵称:"+cik);
System.out.println("你的年龄:"+age);
RandomAccessFile raf = new RandomAccessFile("register.txt","rw");
//先将指针移动到文件末尾
raf.seek(raf.length());
//写用户名
byte[] data = name.getBytes("UTF-8");
data = Arrays.copyOf(data, 32);
System.out.println("写入前的指针位置:"+raf.getFilePointer());
raf.write(data);
System.out.println("写入后的指针位置:"+raf.getFilePointer());
//写密码
data = password.getBytes("UTF-8");
data = Arrays.copyOf(data, 32);
raf.write(data);
//写昵称
data = cik.getBytes("UTF-8");
data = Arrays.copyOf(data, 32);
raf.write(data);
//写年龄
System.out.println("写入前的指针位置:"+raf.getFilePointer());
raf.writeInt(age);
System.out.println("写入前的指针位置:"+raf.getFilePointer());
raf.close();
}
}
# 读取所有注册用户信息
package raf;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* 列出所有注册用户信息
* @author chengyi
*
*/
public class Demo3 {
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile("register.txt","r");
for(int i=0; i<raf.length()/100; i++){
byte[] date = new byte[32];
//读取用户名
raf.read(date);
String name = new String(date,"UTF-8");
System.out.println("名字:"+name);
//读取密码
raf.read(date);
String password = new String(date,"UTF-8");
System.out.println("密码:"+password);
//读取昵称
raf.read(date);
String cik = new String(date,"UTF-8");
System.out.println("昵称:"+cik);
//读取年龄
int age = raf.readInt();
System.out.println("年龄:"+age);
System.out.println("pos"+raf.getFilePointer());
}
}
}
# 登录验证
package raf;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Scanner;
/**
* 登录验证
*
* 程序启动后,要求用户输入用户名及密码
* 然后匹配user.dat文件中的用户,若用户名及密码输入正确
* 提示登录成功,否则提示用户名或密码不正确
* @author chengyi
* 注意思考trim的重要性
*/
public class Demo4 {
public static void main(String[] args) throws IOException {
Scanner scan = new Scanner(System.in);
System.out.println("请输入用户名:");
String inputname = scan.nextLine();
System.out.println("请输入密码!");
String password = scan.nextLine();
RandomAccessFile raf = new RandomAccessFile("register.txt","r");
byte[] bt = new byte[32];
for(int i=0; i<raf.length()/100; i++){
raf.seek(i*100);
raf.read(bt);
String usename = new String(bt,"UTF-8").trim();
if(usename.equals(inputname)){
System.out.println("用户名正确");
raf.read(bt);
String usePassword = new String(bt,"UTF-8").trim();
if(usePassword.equals(password)){
System.out.println("登录成功");
}else{
System.out.println("密码错误");
}
break;
}
if((raf.length()/100-i)<2){
System.out.println("用户名错误");
}
}
raf.close();
}
}
# java标准IO
java标准IO I:inout O:output 输入流 输出流 用来读 用来写
# FileOutputStream
package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
/**
* java标准的IO操作 Input,Output 输入与输出
* 流按照功能分为读写,按照方向分为输入,输出。而方向的参照物为我们写的程序
* 输入流用来读取数据。
* 输出流用来写出数据。
*
* java.io.InputStream是所有字节输入流的父类,规定了所有输入流都应当
* 具备的读取字节数据的相关方法
*
* java.io.OutputStream 是所有字节输出流的父类。
*
* java将流分为两大类:节点流和处理流
* 节点流:又称为低级流,是实际连接程序与另一端的“管道”,负责实际搬运数据。
* 读写一定是建立在低级流的基础上运行的。
*
* 处理流:又称为高级流,不能独立存在(没意义),高级流一定会连接在其他流上,
* 使得数据“流经”该流时对其进行加工处理,简化我们对数据读写时的某些操作。
*
* 文件流
* 文件流是一种低级流,用于读写文件数据。功能与RAF一样。但是底层的读写方式不同。流是
* 顺序读写的。而RAF是基于指针随机读写的。
* @author chengyi
*
*/
public class FileOutputStreamDemo1 {
public static void main(String[] args) throws UnsupportedEncodingException, IOException {
/*
* 向文件fos.txt中写出字符串
* 流有两种常见创建形式:
* FileOutputStream(String path)
* FileOutputStrean(File file)
* 以上两种创建是覆盖模式,即若要操作的文件已经存在,
* 会先将该文件数据清除,然后通过该流写出的数据作为该文件数据。
*
* FileOutputStream(String path,boolean append)
* FileOutputStream(File file, boolean append)
* 当第二个参数为true时,该流为追加写模式,即该文件原有数据全部保留,
* 通过流写出的数据会被追加到文件后面继续写。
*/
FileOutputStream fos = new FileOutputStream("fos.txt",true);
String str= "浪花里舞蹈";
fos.write(str.getBytes("UTF-8"));
System.out.println("写出完毕");
fos.close();
}
}
# FileInputStream
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* 文件输入流,用于读取文件数据
* @author chengyi
*/ 注意string重载的构造方法
public class FileInputStreamDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("fos.txt");
byte[] data = new byte[10];//创建一个比文件大的字节数组
//获取返回值,得到实际读取的字节量
int len = fis.read(data);
String str = new String(data,0,len,"UTF-8");
System.out.println(str);
fis.close();
}
}
# BufferedInputStream&BufferedOutputStream
package io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 缓冲流:缓冲流是一对高级流
* java.io.BufferedOutputStream
* java.io.BufferedInputStream
* 缓冲字节输入输出流是用来加快读写效率的。
*
* 使用缓冲流完成复制操作
* @author chengyi
*
*/
public class CopyDemo2 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("DG.mp4");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("DG_cp.mp4");
BufferedOutputStream bos = new BufferedOutputStream(fos);
long star = System.currentTimeMillis();
int d;
/*
* 使用了缓冲流读写时,我们就不再需要关注必须用块读写加快
* 效率了。因为缓冲流内部维护了一个字节数组,最终会将我们的读写
* 操作转换为块读写加快读写效率。
*/
while((d=bis.read())!=-1){
bos.write(d);
}
long end = System.currentTimeMillis();
System.out.println("花费时间"+(end-star));
System.out.println("复制完成");
bis.close();
bos.close();
}
}
# 缓冲区问题
package io;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
/**
* 缓冲输出流写数据时的缓冲区问题
* @author chengyi
*
*/
/*
* 注意引进的类名和自己所要定义的名字不能相同
*/
public class BufferedOutputStream_flush {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("bos.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
String str = "夜空中最亮的星,你是否清醒。";
byte[] data = str.getBytes("UTF-8");
int d = 0;
bos.write(data);
//强制将缓冲区数据一次性写出
bos.flush();
System.out.println("输入完成");
bos.close();
}
}
# Serializable
package io;
import java.io.Serializable;//实现这个接口需要引进
import java.util.Arrays;
/**
* 使用当前类的实例 测试对象流读写对象的操作
*
* 当一个类的实例要求被对象流进行读写时,要求该类必须实现Serializable接口
* @author chengyi
*
*/
public class Person implements Serializable{
/** //这个接口里面什么也没有,叫签名接口
* 当一个类实现了Serializable接口后,应当定义一个常量:serialVersionUID
* 这个常量是序列化版本号。若不指定,编译器会在编译时按照当前类
* 的结构生成一个版本号。但是若类的结构发生改变,版本号会跟着改变。
* 序列化版本号直接影响对象输入流进行反序列化是否能成功。
* 当反序列化的对象对当前类版本号一致,那么反序列化成功,否则反序列化时会抛出异常
* 若当前类结构发生了变化,只要版本号没有改变,那么反序列化时会将仍然
* 有的属性进行还原。
*/
private static final long serialVersionUID = 1L;
private String name;
private int age;
private String gender;
/*
* 当一个属性使用transient修饰后,那么进行序列化时,该属性的值会被忽略。
* 忽略不必要的属性可以达到对象序列化的“瘦身”操作。
*/
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 toString(){
return name+","+age+","+gender+","+Arrays.toString(otherInfo);
}
}
# ObjectOutputStream
package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/**
* 对象流
* 对象流是一对高级流,可以方便我们读写Java中的任何对象。
* 对象输出流:可以将指定的对象转换为一组字节后写出
* 对象输入流:可以将一组字节还原为对应的对象,前提是这组字节应当是
* 对象输出流将一个对象转换的字节。
* @author chengyi
*
*/
public class ObjectOutputStreamDemo {
public static void main(String[] args) throws IOException {
String name = "程意";
int age = 18;
String gender = "男";
String[] other = {"伟大的程序员","大将军","测量员"};
Person p = new Person(name,age,gender,other);
/*
* 将person对象写入到文件person.obj中
*/
FileOutputStream fos = new FileOutputStream("person.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
/*
* 对象输出流提供的方法
* void writeObject(Object o)
* 可以将给定的对象转换为一组字节后写出
*
* 下面代码实际做了两件事
* 1:p对象先流经对象输出流,而对象输出流将该对象转换为了
* 一组字节,这个过程称为对象序列化
*
* 2:转换的这组字节在流经文件输出流,然后写入了文件保存(写入磁盘),将数据写入
* 磁盘做长久保存的过程称为数据持久化
*/
oos.writeObject(p);
System.out.println("输入完成");
oos.close();
}
}
# ObjectInputStream
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
/**
* 对象输入流,用于进行对象反序列化操作
* 需要注意,对象流读取的字节必须是由对象输出流将一个对象序列化后
* 的字节,否则进行反序列化时会抛出异常
* @author chengyi
*
*/
public class ObjectInputStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream("person.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
Person p = (Person) ois.readObject();
System.out.println(p.toString());//这里为什么只写p也可以
ois.close();
}
}
# OutputStreamWriter
package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
/**
* java 将流按照读写单位划分为字节流和字符流
*
* 字符流:读写单位是以字符为单位,所以字符流读写数据有一定的局限性
* 只能用于读写文本数据。非文本数据不能使用字符流读取(如:图片,
* 基本数据类型,mp3等数据)
*
* java.io.Reader, java.io.Writer
* Reader是所有字符输入流的父类
* Writer是所有字符输出流的父类
*
* 转换流:
* java.io.InputStreamReader
* java.io.OutputStreamWriter
* 他们是一对高级流,同时也是常用的字符流实现类。
* 在读写文本数据时,使用高级流进行流连接中是非常重要的一环,
* 起到承上启下的作用。因为几乎所有的字符流只能连接在其他字符流上,而基本上低
* 级流都是字节流,由于转换流可以连接字节流,而本身其自身又是字符流,所以起到
* 将字符流与字节流“对接”的作用
* @author chengyi
*
*/
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("osw.txt");
//注意这里可以传双参,指定字符集的名字
OutputStreamWriter osw = new OutputStreamWriter(fos,"GBK");
osw.write("摩擦摩擦,似魔鬼的步伐。");
osw.write("在光滑的马路牙子上打出溜儿滑。");
System.out.println("写出完毕!");
osw.close();
}
}
# InputStreamReader
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
/**
* 转换流
* InputStreamReader,可以读取字符
* @author chengyi
*
*/
public class InputStreamReaderDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("osw.txt");
InputStreamReader isr = new InputStreamReader(fis,"GBK");
/*
int d = -1;
while((d=isr.read())!=-1){
System.out.print((char)d);
}
*/
char[] data = new char[30];
int len =-1;
while((len = isr.read(data))!=-1){
String str = new String(data,0,len);
System.out.println(str);
}
isr.close();
}
}
# PrintWriter
package io;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
/**
* 缓冲字符流
* java.io.BufferedWriter
* java.io.BufferedReader
* 内部有缓冲区,可以块读写字符。并且可以按行读写字符串。
*
* java.io.PrintWriter
* 具有自动行刷新的缓冲字符输出流,内部总是会连接BufferedWriter作为缓冲操作。
* @author chengyi
*
*/
public class PrintWriterDemo1 {
public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
/*
*Printwriter提供了直接对文件写操作的构造方法
*PrintWriter的构造方法就是一个流连接
*PrintWriter(String path)
*PrintWriter(File file)
*/
//向pw.txt文件中写出字符串
PrintWriter pw = new PrintWriter("pw.txt","GBK");
//只有在使用println输出时,会自动换行
pw.println("你在南方的艳阳里,大雪纷飞。");
pw.println("我在北方的寒夜里,四季如春。");
System.out.println("输出完毕");
pw.close();
}
}
# BufferedWriter
package io;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
/**
* 在流连接中使用PrintWriter
* @author chengyi
*
*/
public class PrintWriterDemo2 {
public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
FileOutputStream fos= new FileOutputStream("pw2.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
//字符的块读写
BufferedWriter bw = new BufferedWriter(osw);
PrintWriter pw = new PrintWriter(bw);
pw.println("随便写了");
pw.println("不想想了");
System.out.println("写出完毕");
pw.close();
}
}
package io;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Scanner;
/**
* 使用PrintWriter,用流连接形式创建。
* 完成记事本功能。
* 将控制台输入的每一行字符串写入文件note.txt中
* @author chengyi
*
*/
public class PrintWriterDemo3 {
public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
Scanner scan = new Scanner(System.in);
System.out.println("请输入内容,输入exist退出");
/*
* PrintWriter的构造方法若第一个参数是一个流,
* 那么就支持第二个参数,该参数为boolean型,若值为true时,
* 那么PW就具有Println方法写出一行字符串后会自动flush。
* 注意,调用print方法是不会自动刷新的,必须println!
*/
/*
FileOutputStream fos = new FileOutputStream("note.txt",true);
OutputStreamWriter osw = new OutputStreamWriter(fos);
BufferedWriter bw = new BufferedWriter(osw);
PrintWriter pw = new PrintWriter(bw,true);
*/
PrintWriter pw = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("note.txt"),
"UTF-8")
),true
);
boolean flag = true;
String str = null;
while(flag){
str = scan.nextLine();
if("exit".equals(str)){
//flag = false;
break;
}
//自动行刷新的话,这里会自动flush
pw.println(str);
}
pw.close();
}
}
# BufferedReader
package io;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* java.io.BufferedReader
* 缓冲字符输入流,可以按行读取字符串
* @author chengyi
*
*/
public class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
/*
* 将当前源代码读取出来并输出到控制台
*/
FileInputStream fis = new FileInputStream("./src/io/BufferedReaderDemo.java");
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
/*
* BufferedReader提供了直接读取一行字符串的方法:
* String readLine()
* 该方法会连续读取若干字符,当读取到换行符时,将之前读取的字符以字符串
* 形式返回,若返回值为null时,表示流的末尾
*/
String line = null;
while((line=br.readLine())!=null){
System.out.println(line);
}
br.close();
}
}
# 异常
exception 程序错误 error 系统级别的错误
# try-catch
package exception;
/**
* java异常处理机制中的try-catch
* 语法:
* try{
* 代码片段
* }catch(XXXException e){
* 捕获try代码片段中出现的XXXException后放入处理手段
* }
* @author chengyi
*
*/
public class TryCatchDemo {
public static void main(String[] args) {
System.out.println("程序开始了");
try{
String str = "a";
/*
* 当JVM执行代码时遇到异常时,会实例化该异常的一个实例,
* 然后设置好代码执行的过程,并将该异常抛出。
* 如果抛出异常的代码所在方法没有处理异常的能力,异常会抛到当前方法
* 之外,由调用当前方法的代码片段去处理
*/
System.out.println(str.length());
System.out.println(str.charAt(0));
System.out.println(Integer.parseInt(str));
/*
* try代码片段中出错的代码之后内容都不会被运行。
* 如果没有异常发生,catch中的代码就不走了
*/
System.out.println("!!!!!");
}catch(NullPointerException e){
System.out.println("出现了空指针");
/*
* catch可以定义多个,针对不同的异常有不同处理手段时,可以分别
* 捕获这些异常
*/
}catch(StringIndexOutOfBoundsException e){
System.out.println("下标越界被捕获了!");
/*
* 应当有一个好习惯,在最后一个catch中捕获Exception,尽量避免
* 因为一个未捕获的异常导致程序中断。
* 捕获顺序应当是子类型异常在上面先捕获,父类型异常在下后捕获。
*/
}catch(Exception e){
System.out.println("反正就是出了个错");
}
System.out.println("程序结束了");
}
}
# finally
package exception;
/**
* finally块是异常捕获机制中的最后一块。
* finally可以直接跟在try后面或者最后一个catch之后。
* finally可以保证只要程序执行到try语句快中,无论try语句块中的代码是否
* 抛出异常,finally块中的代码都必定执行。
* 通常会将无论是否出现异常都要运行的代码放在finally中确保运行,比如IO
* 操作中的关闭流。
* @author chengyi
*
*/
public class FinallyDemo1 {
public static void main(String[] args) {
System.out.println("程序开始了");
try{
String str = "";
System.out.println(str.length());
//Finally执行完,方法才能返回(所以“程序结束”不会被输出)
return;
}catch(Exception e){
System.out.println("异常被捕获了");
}finally{
System.out.println("finally中的代码运行了");
}
System.out.println("程序结束了");
}
}
# 异常处理机制在io中的使用
package exception;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* 异常处理机制在IO中的使用
* @author chengyi
*
*/
public class FinallyDemo2 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("fos.txt");
int d = fis.read();
System.out.println(d);
} catch (IOException e) {
e.printStackTrace();
} finally{
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
# 自动关闭
package exception;
import java.io.FileInputStream;
import java.io.IOException;
/**
* JDK1.7之后推出了一个新特性:自动关闭
* @author chengyi
*
*/
public class FinallyDemo3 {
public static void main(String[] args) {
try(
/*
* 实现了AutoCloseable接口的可以定义在这里
* 该特性是编译器认可的,最终编译器还是会将代码改为
* 在Finally中关闭该流。
*/
FileInputStream fis = new FileInputStream("fos.txt");
){
int d = fis.read();
System.out.println(d);
}catch(IOException e){
e.printStackTrace();
}
}
}
package exception;
/**
* 使用当前类测试异常的抛出
* @author chengyi
*
*/
public class Person {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) throws Exception {
if(age<0||age>100){
/*当不满足业务逻辑要求时,可以主动抛异常
* 当一个方法中使用throw抛出一个异常时就应当在当前方法
* 上使用throws声明该异常的抛出,通知调用者在调用当前方
* 法时要处理异常(RuntimeException除外)否则编译不通过。
*/
throw new Exception("年龄不合法");
}
this.age = age;
}
}
package exception;
/**
* 测试异常的抛出
* @author chengyi
*
*/
public class ThrowDemo {
public static void main(String[] args) {
System.out.println("程序开始了");
Person p = new Person();
try {
/*
* 当调用一个含有throws声明异常抛出的方法时,
* 编译器要求必须处理这个异常,而处理的方式有两种:
* 1:使用try-catch处理异常
* 2:在当前方法上继续声明throws将该异常抛出
* 具体如何处理,结合实际业务需求而定。
*/
p.setAge(10000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("此人的年龄:"+p.getAge());
System.out.println("程序结束了");
}
}
# 自定义异常
package exception;
import java.awt.AWTException;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* 派生类在继承超累时会重写其方法,那么在重写超类中含有throws声明异常
* 抛出的方法时,对throws的重写规则
* @author chengyi
*
*/
public class ThrowsDemo {
public void dosome() throws IOException,AWTException{
}
}
class son extends ThrowsDemo{
// public void dosome() throws IOException,AWTException{
//
// }
//可以不在抛出任何异常
// public void dosome(){
//
// }
//可以仅抛出父类方法抛出的部分异常
// public void dosome() throws IOException{
//
// }
//可以抛出父类方法抛出异常的子类型异常
public void dosome() throws FileNotFoundException{
}
//不允许抛出父类方法抛出异常的父类型异常
// public void dosome() throws Exception{
//
// }
}
package exception;
/**
* 异常常用方法
* @author chengyi
*
*/
public class ExceptionApiDemo {
public static void main(String[] args) {
System.out.println("程序开始了");
try{
String str = "a";
System.out.println(Integer.parseInt(str));
}catch(Exception e){
//输出错误堆栈信息,有助于定位错误并解决
e.printStackTrace();
String message = e.getMessage();
System.out.println(message);
}
System.out.println("程序结束了");
}
}
package exception;
/**
* 年龄不合法异常
*
* 自定义异常通常用来说明某些业务逻辑错误。
* @author chengyi
*
*/
public class IllegalAgeException extends Exception{
private static final long serialVersionUID = 1L;
public IllegalAgeException() {
super();
// TODO Auto-generated constructor stub
}
public IllegalAgeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
}
public IllegalAgeException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public IllegalAgeException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
public IllegalAgeException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}
然后在Person类中改成抛出此异常,在ThrowDemo中try-catch就会自动生成IllegalAgeExcepion
# 线程
# 创建线程
- 继承Thread并重写run方法
package thread;
/**
* 线程能允许我们“同时执行"多段代码。
* 线程有两种创建方式
* 1:继承Thread并重写run方法,在run方法中定义线程要执行的任务
* @author chengyi
*
*/
public class ThreadDemo1 {
public static void main(String[] args){
MyThread1 mt1 = new MyThread1();
MyThread2 mt2 = new MyThread2();
/*
* 线程启动要调用start,而不是直接调用run方法。
* start方法的作用是将线程纳入线程调度中,线程调度会统一管理需要
* 并发运行的多个线程,调度CPU,分配时间片段给线程,得到时间片段
* 的线程会被CPU运行这段时间,运行完毕后,线程调度会再分配时间
* 片段给一个线程使CPU运行该线程。
* 线程调度分配时间片段给每个线程并非有序的“一人一次"
* 但是在整体的过程中,每个线程获取的CPU时间片段的次数是基本一致的。
*/
mt1.start();
mt2.start();
}
}
/**
* 第一种创建线程方式存在两种不足:
* 1:由于java是单继承的,这就导致在实际开发中我们往往需要继承某个类复
* 用方法,而当前类又需要并发运行,导致不能同时又继承复用方法的类又继承
* 线程。
* 2:定义线程的同时重写run方法定义任务,这就导致了线程和任务有一个必然
* 的耦合关系。不利于线程的重用。
*
* @author chengyi
*
*/
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("我是查水表的");
}
}
}
- 单独定义线程任务
package thread;
/**
* 第二种创建线程的方式:单独定义线程任务
* 实现Runnable接口并重写run方法
* @author chengyi
*
*/
public class ThreadDemo2 {
public static void main(String[] args){
//实例化线程任务
MyRunnable1 mr1 = new MyRunnable1();
MyRunnable2 mr2 = new MyRunnable2();
//创建线程
Thread th1 = new Thread(mr1);
Thread th2 = new Thread(mr2);
th1.start();
th2.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("我是查水表的");
}
}
}
# currentThread
package thread;
/**
* 线程提供了一个静态方法:
* static Thread currentThread()
* 该方法可以获取运行该方法的线程。
* @author chengyi
*
*/
public class CurrentThreadDemo {
public static void main(String[] args){
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(){
Thread t = Thread.currentThread();
System.out.println("运行dosome方法的线程是:"+t);
}
}
# getThreadInfo
package thread;
/**
* 获取线程相关信息的一组方法
* @author chengyi
*
*/
public class ThreadDemo4 {
public static void main(String[] args){
Thread main = Thread.currentThread();
//获取线程的名字
String name = main.getName();
System.out.println("name:"+name);
//获取唯一标识
long id = main.getId();
System.out.println("id:"+id);
//获取线程的优先级
int priority = main.getPriority();
System.out.println("优先级:"+priority);
//是否运行着
boolean isAlive = main.isAlive();
System.out.println("isAlive:"+isAlive);
boolean isDaemon = main.isDaemon();
System.out.println("是否为守护线程:"+isDaemon);
boolean isInterrupted = main.isInterrupted();
System.out.println("是否被中断:"+isInterrupted);
}
}
```
### setPriority
```java
package thread;
/**
* 线程优先级
* 线程之所以能得以并发运行,是靠线程调度的工作,线程调度会分配CPU时间
* 片段给某个线程,使得其可以运行。线程不能主动获取CPU时间。
* 调整线程的优先级可以最大程度的改善某个线程获取CPU时间片的次数,理论上
* 线程优先级越高的线程获取CPU时间片的次数越多。
* 线程的优先级由数字1-10表示。1是最低优先级,10为最高优先级,5为默认值。
* @author chengyi
*
*/
public class PriorityDemo {
public static void main(String[] args) {
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("norm");
}
}
};
max.setPriority(Thread.MAX_PRIORITY);
min.setPriority(Thread.MIN_PRIORITY);
min.start();
norm.start();
max.start();
}
}
# sleep
package thread;
/**
* sleep阻塞
* 线程提供的方法:
* static void sleep(long ms)
* 该方法可以让运行该方法的线程处于阻塞状态指定毫秒
* 超时后线程会重新回到RUNNABLE状态,等待分配CPU时间片再次运行。
* @author chengyi
*
*/
public class SleepDemo {
public static void main(String[] args){
System.out.println("程序开始了");
try {
/*
* 通常会引起线程阻塞现象的方法都要求处理中断异常。
* 线程有一个方法:interrupt()
* 该方法可以中断一个正在运行的线程。但是若该线程正处于某种
* 阻塞时被调用中断方法中断,那么并不是将该线程直接中断,而是
* 中断其阻塞状态。这时通常会抛出中断异常,通知程序该线程的
* 阻塞状态被打断。
*/
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("程序结束了");
}
}
# interrupt
package thread;
/**
* 打断睡眠阻塞
* @author chengyi
*
*/
public class SleepDemo2 {
public static void main(String[] args) {
Thread lin = new Thread(){
public void run(){
System.out.println("林:刚美完容,睡一会吧!");
try {
Thread.sleep(5000000);
} catch (InterruptedException e) {
}
System.out.println("林:干嘛那!干嘛那!这是干嘛那!");
}
};
Thread huang =new Thread(){
public void run(){
for(int i=0;i<5;i++){
System.out.println("huang:80!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("huang:咣当!");
System.out.println("huang:搞定!");
lin.interrupt();//打断lin线程
}
};
lin.start();
huang.start();
}
}
# setDaemon
package thread;
/**
* 守护线程
* 守护线程又称为后台线程,一个线程创建出来默认都是普通(前台线程),
* 守护线程需要在线程启动前单独进行设置。
* 使用上守护线程与普通线程无差别,但是在结束时机上有一点不同,
* 即:在进程结束时,所有正在运行的守护线程都会被强制中断。
* 进程结束:当一个进程中的所有前台线程都结束时,进程即结束
* @author chengyi
*
*/
public class DaemonThreadDemo {
public static void main(String[] args) {
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) {
e.printStackTrace();
}
}
System.out.println("rose:啊啊啊...救命啊!");
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) {
e.printStackTrace();
}
}
}
};
rose.start();
//设置为守护线程,必须在star之前调用
jack.setDaemon(true);
jack.start();
//main 方法也是线程,当运行到rose,jack线程时,rose和Jack线程
//会与main线程同步执行,所以下面的"main线程结束"并不是在最后执行
System.out.println("main线程结束");
}
}
# join
package thread;
/**
* join方法协调线程之间的同步运行。
* join方法会让运行该方法的线程处于阻塞状态,直到该方法所属线程运行完毕
* 才会解除阻塞。
* @author chengyi
*
*/
public class JoinDemo {
private static boolean isFinish = false;
public static void main(String[] args) {
Thread down = new Thread(){
public void run(){
System.out.println("down:开始下载图片");
for(int i=0; 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 {
down.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
if(!isFinish){
throw new RuntimeException("图片加载不成功");
}
System.out.println("show:图片显示完毕!");
}
};
down.start();
show.start();
}
}
# 线程安全
package thread;
/**
* 当多个线程并发操作同一资源时就形成了“抢”的局面,由于线程切换不确定,
* 可能会导致线程操作该资源时未按照程序预定的执行顺序执行代码,导致逻
* 辑出现混乱。
* 严重时可能导致系统瘫痪。
* @author chengyi
*
*/
public class SyncDemo1 {
public static void main(String[] args) {
Table table = new Table();
Thread t1 = new Thread(){
public void run(){
while(true){
int beans = table.getBean();
Thread.yield();//让出CPU,模拟线程切换
System.out.println(getName()+":"+beans);
}
}
};
Thread t2 = new Thread(){
public void run(){
while(true){
int beans = table.getBean();
Thread.yield();//让出CPU,模拟线程切换
System.out.println(getName()+":"+beans);
}
}
};
t1.start();
t2.start();
}
}
class Table{
//一个桌子上有20个豆子
private int beans = 20;
/*
* 当一个方法被synchronized修饰后,那么该方法称为同步方法,
* 即:多个线程不能同时在方法内部执行,从而解决了并发安全问题。
*
* 在方法上使用synchronzied, 那么同步监视器对象那个就是当前方法
* 所属对象,即:方法内部看到的this
*/
public synchronized int getBean(){
if(beans == 0){
throw new RuntimeException("没有豆子");
}
Thread.yield();//让出CPU,模拟线程切换
return beans--;
}
}
# synchonized
package thread;
/**
* 有效的缩小同步范围可以在保证并发安全的前提下尽可能提高并发效率。
*
* 同步块可以更准确的控制需要同步运行的代码片段。
* synchonized(同步监视器){
* 需要同步运行的代码片段
* }
*
* 同步监视器是java中任意的一个对象,只要保证多个线程看到的该对象是“同一个“,
* 即可保证同步块中的代码是并发安全的。
* @author chengyi
*
*/
public class SyncDemo2 {
public static void main(String[] args) {
Shop s = new Shop();
Thread t1 = new Thread(){
public void run(){
s.buy();
}
};
Thread t2 = new Thread(){
public void run(){
s.buy();
}
};
t1.start();
t2.start();
}
}
class Shop{
// public synchronized void buy(){
public void buy(){
Thread t = Thread.currentThread();
try {
System.out.println(t.getName()+":正在挑衣服...");
Thread.sleep(5000);
synchronized(this){
System.out.println(t.getName()+":正在试衣服...");
Thread.sleep(5000);
}
System.out.println(t.getName()+":结账离开.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- synchonized修饰静态方法
package thread;
/**
* 静态方法若使用synchronized修饰后,那么该方法一定具有同步效果。
* @author chengyi
*
*/
public class SyncDemo3 {
public static void main(String[] args) {
Thread t1 = new Thread(){
public void run(){
Foo.dosome();
}
};
Thread t2 = new Thread(){
public void run(){
Foo.dosome();
}
};
t1.start();
t2.start();
}
}
class Foo{
public synchronized static void dosome(){
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在执行dosome");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
System.out.println(t.getName()+":运行dosome完毕");
}
}
- 互斥锁
package thread;
/**
* 互斥锁
* 当使用synchronized锁住多段不同的代码片段,但是这些同步块使用的同步
* 监视器 对象 是同一个时,那么这些代码片段之间就是互斥的。多个线程不能
* 同时执行他们。
* @author chengyi
*
*/
public class SyncDemo4 {
public static void main(String[] args) {
Boo b = new Boo();
Thread t1 = new Thread(){
public void run(){
System.out.println(getName());
b.methodA();
}
};
Thread t2 = new Thread(){
public void run(){
b.methodB();
}
};
t1.start();
t2.start();
}
}
class Boo{
public synchronized void methodA(){
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在运行A方法");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t.getName()+"运行A方法完毕");
}
public synchronized void methodB(){
Thread t = Thread.currentThread();
System.out.println(t.getName()+"正在运行B方法");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t.getName()+"运行B方法完毕");
}
}
- 死锁
package thread;
/**
* 死锁现象
* 当多个线程都持有自己的锁,但是都等对方先释放锁时,就就出现“僵持”的情况,
* 使得所有线程进入阻塞状态。这个现象称为死锁现象。
* @author chengyi
*
*/
public class SyncDemo5 {
public static void main(String[] args) {
Coo coo = new Coo();
Thread t1 = new Thread(){
public void run (){
coo.methodA();
}
};
Thread t2 = new Thread(){
public void run(){
coo.methodB();
}
};
t1.start();
t2.start();
}
}
class Coo{
private Object lockA = new Object();
private Object lockB = new Object();
public void methodA(){
Thread t = Thread.currentThread();
synchronized(lockA){
System.out.println(t.getName()+":持有lockA的锁,并开始运行A方法");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t.getName()+"开始运行B方法");
methodB();
}
System.out.println(t.getName()+":释放lockAde的锁,并执行A方法完毕");
}
public void methodB(){
Thread t = Thread.currentThread();
synchronized(lockB){
System.out.println(t.getName()+":持有lockB的锁,并开始运行A方法");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t.getName()+":开始运行A方法");
methodA();
}
System.out.println(t.getName()+":释放lockB的锁,并执行B方法完毕");
}
}
# 集合
# add
package collection;
import java.util.ArrayList;
import java.util.Collection;
/**
* java.util.Collection
* Collection是所有集合的顶级接口,规定了所有集合都应当具备的方法。
* 其下有两个常用的派生接口:
* java.util.List:可重复集合,并且有序。
* java.util.Set:不可重复集合。
* 重复指的是元素是否可以重复,而重复的标准是元素自身equals比较的结果是否为true.
* @author chengyi
*
*/
public class CollectionDemo1 {
public static void main(String[] args) {
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()
* 判断当前集合是否为空集(不含有任何元素)
*/
boolean isEmpty = c.isEmpty();
System.out.println("isEmpty:"+isEmpty);
/*
* 清空集合
*/
c.clear();
System.out.println(c);
System.out.println("size:"+c.size());
System.out.println("isEmpty:"+c.isEmpty());
}
}
# contains
package collection;
import java.util.ArrayList;
import java.util.Collection;
/**
* boolean contains(E e)
* 判断当前集合是否包含给定元素
* @author chengyi
*
*/
public class ContainsDemo {
public static void main(String[] args) {
Collection c = new ArrayList();
//括号里面new的对象直接就经过了重写的toString
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);
/*
* 集合是用给定元素与集合现有元素进行equals比较,若有比较为true的则
* 认为集合包含该元素。所以元素的equals方法直接影响集合的contains
* 方法的结果。如果不重写equals那就直接用的是Object的equals
*/
boolean contains = c.contains(p);
System.out.println("contains:"+contains);
}
}
# delete
package collection;
/**
* 使用当前方法作为集合元素,测试集合操作的相关方法
* @author chengyi
*
*/
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;
}
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;
}
public String toString(){
return "("+x+","+y+")";
}
}
package collection;
import java.util.ArrayList;
import java.util.Collection;
/**
* 删除集合元素
* @author chengyi
*
*/
public class RemoveDemo {
public static void main(String[] args){
Collection c = new ArrayList();
c.add("(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);
/*
* 只删除一个元素
* 删除也是依靠元素equals比较的结果
*/
c.remove(p);
System.out.println(c); //[(1,2), (3,4), (5,6), (7,8), (1,2)]
//[(1,2), (3,4), (5,6), (7,8)]
c.remove("(1,2)");
System.out.println(c); //[(3,4), (5,6), (7,8)]
}
}
# 集合存放的是元素的引用
package collection;
/**
* 集合存放的是元素的引用
*/
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo2 {
public static void main(String[] args) {
Collection c = new ArrayList();
Point p = new Point(1,2);
int a =1;
c.add(p);
c.add(a);
System.out.println(c);//[(1,2),1]
System.out.println(p);//(1,2)
a = a+1;
p.setX(2);
System.out.println(a);//2
System.out.println("c:"+c);//[(2,2),1]
System.out.println("p:"+p);//(2,2)
}
}
# addAll&removeAll
1.package collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
/**
* 集合间的操作
* @author chengyi
*
*/
public class CollectionDemo3 {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("java");
c.add("c");
c.add(".net");
System.out.println("c:"+c);//c:[java, c, .net]
Collection c2 = new HashSet();
c2.add("php");
c2.add("android");
c2.add("java");
System.out.println("c2"+c2);//c2[java, android, php]
/*
* boolean addAll(Collection c)
* 将给定集合中的所有元素添加到当前集合中
*/
c.addAll(c2);
c2.addAll(c );
System.out.println("c"+c);//c[java, c, .net, java, android, php
System.out.println("c2"+c2);//c2[java, c, android, php, .net]
Collection c3 = new ArrayList();
c3.add("c");
c3.add("php");
c3.add("c++");
System.out.println("c3:"+c3);//c3:[c, php, c++]
/*
* boolean containsAll(Collection c)
* 判断当前集合是否包含给定集合中的所有元素
*/
boolean containsAll = c.containsAll(c3);
System.out.println("全包含:"+containsAll); //false
/**
* boolean removeAll(Collection c)
* 删除当前集合中与给定元素的交集部分
*/
c.removeAll(c3); //这是删除c的
System.out.println("c:"+c); //c[java, .net, java, android]
}
}
```
### 迭代器
```java
package collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* Collection提供了统一的获取元素的操作:遍历
* 而遍历集合使用的是迭代器模式:
* Iterator iterator()
* 该方法获取一个可以用于遍历当前集合的迭代器
*
* java.util.Iterator
* Iterator是一个接口,定义了迭代器遍历集合的相关操作方法。不同的集合都提供了
* 一个迭代器实现类。
*
* 迭代器遍历集合遵循:问,取,删的步骤,其中删除元素不是必要的操作。
* @author chengyi
*
*/
public class IteratorDemo {
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:"+c);
/*
* boolean hasNext()
* 判断当前集合是否还有元素可以迭代
*
* E next()
* 获取集合下一个元素
*/
Iterator it = c.iterator();
while(it.hasNext()){
String str = (String)it.next();
if("#".equals(str)){
/*
* 迭代器要求在遍历集合的过程中不能通过集合的方法增删元素,
* 否则会抛出异常
* 迭代器提供了remove方法,删除的就是通过next遍历出来的元素
* 为什么不能用集合的remove()? 因为现在遍历的是接口
*/
it.remove();
}
System.out.println(str);
}
System.out.println(c);
}
}
# 增强for循环
- 遍历数组
package collection;
/**
* 新循环 自JDK1.5之后推出的一个特性
*
* 新循环是用来遍历集合或数组使用的。
* 新循环又称为:增强for循环,for each
* @author chengyi
*
*/
public class NewForDemo1 {
public static void main(String[] args){
String[] array = {"one","two","three","four"};
for(int i=0; i<array.length; i++){
String str1 = array[i];
System.out.println(str1);
}
/*
* 新循环是编译器认可,而不是JVM认可,编译器会将新循环遍历数组改为普通
* for循环遍历。
*/
for(String str1:array){
System.out.println(str1);
}
}
}
- 遍历集合
package collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* 使用新循环遍历集合
* @author chengyi
*
*/
public class NewForDemo2 {
public static void main(String[] args){
Collection c = new ArrayList();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
System.out.println(c);
/*
* 新循环遍历集合就是迭代器遍历集合。所以在遍历过程中不能通过
* 集合的方法增删元素
*/
// for(Object o: c){
Iterator it = c.iterator(); //
while(it.hasNext()){ //
Object o = it.next(); //
String str = (String)o;
System.out.println(str);
}
}
}
# 泛型
package collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* JDK1.5之后推出了一个特性:泛型
*
* 泛型又称为参数化类型,允许调用者在调用某个类的功能时传入一个或多个类型
* 来定义该类的属性,方法的参数以及返回值类型。这大大的提高了代码的灵活度。
*
* 泛型应用最广泛的地方就是集合,用来指定集合中的元素类型。
* @author chengyi
*
*/
public class TypeDemo {
public static void main(String[] args) {
//泛型的运行就是Object
Collection<String> c = new ArrayList<String>();
//编译器检测实参类型是否符合泛型要求
c.add("one");
c.add("two");
c.add("Three");
c.add("four");
// c.add(1);//编译不通过
System.out.println(c);
Iterator<String> it = c.iterator();
while(it.hasNext()){
//获取元素时无需在造型,编译器会自动添加造型代码
String str = it.next();
System.out.println(str);
}
for(String str: c){
System.out.println(str);
}
}
}
```
### 面试题
```java
package collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* 泛型常见面试题
* @author chengyi
*
*/
public class TypeDemo2 {
public static void main(String[] args) {
Collection<Integer> c1 = new ArrayList<Integer>();
//编译器检测add的参数是否为Integer
c1.add(1);
c1.add(2);
c1.add(3);
//编译不通过,实参与c1的泛型不匹配
// c1.add("four");
//获取集合元素时,编译器会补全代码将元素造型为Integer
for(int i: c1){
System.out.println(i);
}
// Iterator<Integer> it = c1.iterator();
// while(it.hasNext()){
// int i = it.next().intValue();
// System.out.println(i);
// }
System.out.println("c1:"+c1);
/*
* 不指定泛型时,默认为原型Object
* 所以以c2的角度看待c1的集合时,元素就按照Object看待
*/
Collection c2 = c1;
c2.add("false");
System.out.println("c2:"+c2);//c1:[1, 2, 3, false]
//直接输出的话,就相当于调用了重写方法的toString()
System.out.println("c1:"+c1);//c1:[1, 2, 3, false]
//遍历时会出现造型异常
for(int i:c1){
System.out.println(i);
}
}
}
# List接口
package collection;
import java.util.ArrayList;
import java.util.List;
import java.util.LinkedList;
/**
* java.util.List接口
* List是可重复集且有序,提供了一组可以通过下标操作元素的方法。
* 常用实现类:java.util.ArrayList:
* 内部由数组实现,查询效率更好
*
* java.util.LinkedList
* 内部由链表实现,增删效率更好,首尾增删元素效率最好
* @author chengyi
*
*/
public class ListDemo1 {
public static void main(String[] args){
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
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 e)
* 将给定元素设置到指定位置,返回值为原位置对应的元素
* set方法的实际意义是替换元素操作
*/
String old = list.set(1, "2");
System.out.println(list); //[one, 2, three, four]
System.out.println(old); //two
}
}
# 重载add,remove
package collection;
import java.util.ArrayList;
import java.util.List;
/**
* List提供了一对重载的add,remove方法
* @author chengyi
*
*/
public class ListDemo2 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
System.out.println(list);
/*
* void add(int index,E e)
* 将给定元素插入到指定位置
*/
list.add(2, "2");
System.out.println(list);//[one, two, 2, three, four]
/*
* E remove(int index)
* 删除当前集合中指定位置对应的元素并将其返回
*/
String old = list.remove(1);
System.out.println(list);//[one, 2, three, four]
System.out.println("old:"+old);//old:two
}
}
# 获取子集
package collection;
import java.util.ArrayList;
import java.util.List;
/**
* 获取子集
* @author chengyi
*
*/
public class ListDemo3 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
for(int i=0; i<10; i++){
list.add(i);
}
System.out.println(list);
/*
* List subList(int s,int e)
* 获取指定范围内的子集
*/
//获取3-7
List<Integer> subList = list.subList(3, 8);
System.out.println(subList);
System.out.println(list);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
//将子集元中每个素扩大10倍
//[30,40,50,60,70]
for(int i=0; i<subList.size();i++){
int num = subList.get(i)*10;
subList.set(i, num);
}
System.out.println(subList);
/*
* 对子集操作就是对原集合对应元素的操作
*/
System.out.println(list);//[0, 1, 2, 30, 40, 50, 60, 70, 8, 9]
/*
* 这里说明,在存在子集以后,只有对子集的操作不会影响到原集合和子集
* 一旦对原集合有操作,子集立马报错
*/
//删除2-8
list.subList(2, 9).clear();
System.out.println(list);//[0, 1, 9]
// System.out.println(subList);//抛异常
list.add(10);
System.out.println(list);
// System.out.println(subList);//抛异常
list.remove(1);
System.out.println(list);//[0, 9]
// System.out.println(subList);//抛异常
}
}
# 集合与数组的互转
package collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
/**
* 集合转换为数组
* Collection提供了方法:toArray
* 该方法可以将当前集合转换为一个数组
* @author chengyi
*
*/
public class CollectionToArrayDemo {
public static void main(String[] args) {
Collection<String> c = new ArrayList<String>();
c.add("one");
c.add("two");
c.add("three");
System.out.println(c); //[one, two, three]
//jdk1.5之前只有这一种转换方法,但是只能接受为Object类
// Object[] array = c.toArray();
//注意这个传的参数
String[] array =c.toArray(new String[c.size()]);
System.out.println("len"+array.length); //3
System.out.println(Arrays.toString(array));//注意这个静态方法
} //[one, two, three]
}
# 数组转换为集合
package collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 数组转换为集合
* 数组的工具类:Arrays提供了一个静态方法asList
* 该方法可以将给定的数组转换为一个List集合
* @author chengyi
*
*/
public class ArrayToListDemo {
public static void main(String[] args){
String[] array = {"one","two","three","four"};
List<String> list = Arrays.asList(array);
System.out.println(list.size());
System.out.println(list);
/*
* 对数组转换的集合进行操作,就是对原数组对应的操作
*/
list.set(1, "2");
/*
* 增删元素是不支持的操作,因为导致数组扩容或缩容,那样就无法表示原数组了
*/
// list.add("five");
System.out.println("list:"+list);
System.out.println("array:"+Arrays.toString(array));
/*
* 可以自行创建一个集合,然后做操作。
* 所有集合都支持一个参数为Collection的构造方法,作用是在创建当前集合的同时
* 包含给定集合中的所有元素。
*/
List<String> list2 = new ArrayList<String>(list);
System.out.println("list2:"+list2);
list2.add("five");
System.out.println("list2:"+list2);
}
}
# 集合的排序
package collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
/**
* 集合的排序
*
* 集合的工具类:java.util.Collections
* 提供了很多的静态方法,便于操作集合。
* @author chengyi
*
*/
public class Collections_sort {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
Random random = new Random();
for(int i=0; i<10; i++){
list.add(random.nextInt(100));
}
System.out.println(list);
Collections.sort(list);
System.out.println(list);
//乱序
Collections.shuffle(list);
System.out.println(list);
}
}
# 排序自定义元素
package collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 排序自定义元素的集合
* @author chengyi
*
*/
public class Collections_sort2 {
public static void main(String[] args){
List<Point> list = new ArrayList<Point>();
list.add(new Point(5,6));
list.add(new Point(3,6));
list.add(new Point(2,7));
list.add(new Point(8,9));
list.add(new Point(4,2));
System.out.println(list);
/*
* Collections的sort方法在排序集合时
* 要求集合元素必须实现Comparable接口。
*/
Collections.sort(list);
System.out.println(list);
}
}
public class Point implements Comparable<Point>{
......................
public int compareTo(Point o) {
int len = this.x*this.x+this.y*this.y;
int olen = o.x*o.x+o.y*o.y;
return len-olen;
}
}
package collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 使用Collections.sort(List)排序时会存在一些不足之处
* 1:该方法要求集合元素必须实现Comparable接口,这在实际开发中是不太可取的,
* 因为该功能会对我们写的代码有侵入性
* 所谓侵入性是指:当我们调用某个功能时,该功能要求我们为其修改代码,修改的
* 地方越多,侵入性越高。不利于程序的扩展和后期维护。
*
* 2:若元素已经实现了Comparable接口并定义了比较规则,但是该规则不满足我们
* 排序需求时,该方法就无法使用。比如排序字符串,只能按照字符unicode编码排序。
* 排序中文时则没什么意义。
* @author chengyi
*
*/
public class Collections_sort3 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("常老师");
list.add("jack");
list.add("范老师");
list.add("小泽老师");
list.add("rose");
list.add("Ada");
System.out.println(list);//[常老师, jack, 范老师, 小泽老师, rose, Ada]
Collections.sort(list);
System.out.println(list);//[Ada, jack, rose, 小泽老师, 常老师, 范老师]
}
}
# 重载排序方法
package collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Collections提供了一个重载的sort方法:
* static void sort(List list, Comparator c)
* 该方法要求传入一个比较器,然后根据比较器比较的结果对集合进行自然排序。
* 由于该方法要求传入一个额外的比较规则(比较器),所以该方法不在要求元素必须实现
* Comparable接口,从而对元素没有侵入性。并且由于使用该比较规则排序集合,
* 所以也就不在需要元素自身的比较规则,那么元素自身比较规则不满足排序需求的问题
* 也得以解决。
* @author chengyi
*
*/
public class Collections_sort4 {
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,new Comparator<String>(){
public int compare(String o1,String o2){
return o1.length()-o2.length();
}
});
System.out.println(list);
}
}
# 队列
# 描述
package collection;
import java.util.LinkedList;
import java.util.Queue;
/**
* java.util.Queue
* 队列
* 队列可以存放一组元素,但是存取必须按照先进先出原则。
* Queue是一个接口,其继承自Collection,所以队列也具有集合的特性。
* add,size等方法依然可用。
*
* Queue也提供了一组进出对的方法。常用实现类为:
* java.util.LinkedList
*
* @author chengyi
*
*/
public class QueueDemo {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<String>();
//入队操作
queue.offer("one");
queue.offer("two");
queue.offer("three");
queue.offer("foue");
System.out.println(queue);
//出队操作
String str = queue.poll();
System.out.println(str);//one
System.out.println(queue);//[two, three, foue]
//引用队首元素
str = queue.peek();
System.out.println(str);
System.out.println(queue);
/*
* 遍历队列可以使用迭代器(新循环)
* 遍历不会影响队列中的元素
*/
for(String s: queue){
System.out.println(s);
}
System.out.println(queue);
}
}
# 双端队列
package collection;
import java.util.Deque;
import java.util.LinkedList;
/**
* java.util.Deque 双端队列
* Deque接口继承自Queue,双端队列是两端都可以做进出队操作的队列。
* 常用实现类:java.util.LinkedList
* @author chengyi
*
*/
public class DequeDemo {
public static void main(String[] args){
Deque<String> deque = new LinkedList<String>();
deque.offer("one");
deque.offer("two");
System.out.println(deque);
deque.offerFirst("three");
System.out.println(deque);
deque.offerLast("four");
System.out.println(deque);
String str = deque.poll();
System.out.println(str);//three
System.out.println(deque);//[one two four]
str = deque.pollLast();
System.out.println(str);//four
System.out.println(deque);//[one two]
str = deque.pollFirst();
System.out.println(str);//one
System.out.println(deque);//[two]
}
}
# 栈结构
package collection;
import java.util.Deque;
import java.util.LinkedList;
/**
* 栈结构
* 栈可以保存一组元素,存取元素必须遵循先进后出原则
* java.util.Deque支持栈操作,提供了对应的方法
*
* 主要用于实现前进后退
* @author chengyi
*
*/
public class StackDemo {
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);//[four, three, two, one]
String str = stack.pop();
System.out.println(str);//four
System.out.println(stack);//[three, two, one]
for(String s: stack){
System.out.println(s);
}
}
}
# 线程池
package thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程池
* 线程池主要有两个作用:
* 1:控制线程数量
* 每个线程都会占用进程的一部分内存,线程数量过多会导致资源消耗大,
* 由于所有线程都是并发运行的,那么过多的线程也会导致CPU过度切换,
* 导致并发效率变差。
* 2:重用线程
* 频繁的创建销毁线程会给线程调度带来负担,所以也应当重用线程。
*/
public class ThreadPoolDemo {
public static void main(String[] args){
//固定大小线程池
ExecutorService threadPool = Executors.newFixedThreadPool(2);
for(int i=0; i<5; i++){
Runnable r = new Runnable(){
public void run(){
Thread t = Thread.currentThread();
try{
System.out.println(t.getName()+":正在运行任务......");
Thread.sleep(5000);
System.out.println(t.getName()+":运行任务完毕!");
}catch(Exception e){
}
}
};
//将任务指派给线程池
threadPool.execute(r);
System.out.println("将任务指派给线程池");
}
threadPool.shutdown();
System.out.println("线程池已停止");
threadPool.shutdownNow();
System.out.println("已停止线程池....");
}
}
# 日期
# Date
package date;
import java.util.Date;
/**
* java.util.Date
* Date的每一个实例用于表示一个确切的时间。内部维护一个long值,该值
* 为1970年1月1日00:00:00到当前Date所表示的时间之间所经过的毫秒。
* 由于Date设计时存在时区问题,所以大部分操作时间的方法都被声明为
* 过时的,不在建议使用。
* @author chengyi
*
*/
public class DateDemo {
public static void main(String[] args) {
//默认实例化表示当前系统时间
Date date = new Date();
System.out.println(date);
//获取Date内部维护的long值
long time= date.getTime();
time += 1000*60*60*24;
//可以设置一个long指使Date表示该日期
date.setTime(0);
System.out.println(date);
}
}
# SimpleDateFormat
package date;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* java.text.SimpleDateFormat
* SDF可以根据给定的日期格式将字符串与Date之间相互转换。
* @author chengyi
*
*/
public class SimpleDateFormat_format {
public static void main(String[] args){
Date date = new Date();
System.out.println(date);
SimpleDateFormat sdf = new SimpleDateFormat(
" yyyy-MM-dd HH:mm:ss"
);
/*
* String format(Date date)
* 将给定的Date按照当前SDF指定的日期格式转换为
* 对应内容的字符串
*/
String str = sdf.format(date);
System.out.println(str);
}
}
# sdf_parse
package date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 将一个字符串按照对应的日期格式解析为Date
* @author chengyi
*
*/
public class SimpleDateFormat_parse {
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);
}
}
```
### Calendar
```java
package date;
import java.util.Calendar;
import java.util.Date;
/**
* java.util.Calendar 日历类
* Calendar是一个抽象类,规定了操作日期的相关方法。
* 由于不同的日期历法对日期的计算不同,需要靠不同的子类去实现这些方法。
* 常用的实现类:java.util.GregorianCalendar,即阳历。
*
* Calendar提供了一个静态方法:getInstance()可以获取一个当前系统
* 所在地区适用的实现类,大部分获取的都是阳历实现类。
* @author chengyi
*
*/
public class CalendarDemo1 {
public static void main(String[] args){
Calendar calendar = Calendar.getInstance();
/*
* toString可读性差
*/
System.out.println(calendar);
/*
* Calendar提供了一个可以转换为Date的方法:
* Date getTime()
* 将当前Calendar表示的时间以Date形式返回。
*
* 也提供了一个可以让Calendar表示指定日期的方法:
* void setTime(Date date)
* 是当前Calendar表示给定的Date所表示的日期
*/
Date date = calendar.getTime();
System.out.println(date);
}
}
```
### Calendar_get
```java
package date;
import java.util.Calendar;
/**
* Calendar提供了一个方法:
* int get(int field)
* 该方法可以获取指定的时间分量所对应的值。
*
* 时间分量是一个int值,不同值表示不同时间分量,无需
* 记忆这些数具体是多少,Calendar提供了对应的常量。
* @author chengyi
*
*/
public class Calendar_get {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
//获取年
int year = calendar.get(Calendar.YEAR);
//获取月 月从0开始
int month = calendar.get(Calendar.MONTH)+1;
/*
* 获取日
* 和"天"相关的常量有:
* DAY_OF_MONTH:月中的天,就是俗称的"几号"
* DAY_OF_WEEK:周中的天,就是指星期几
* DAY_OF_YEAR:年中的天。
* DATA:与DAY_OF_MONTH意义相同。
*/
int date = calendar.get(Calendar.DATE);
System.out.println(year+"-"+month+"-"+date);
int h = calendar.get(Calendar.HOUR_OF_DAY);
int m = calendar.get(Calendar.MINUTE);
int s = calendar.get(Calendar.SECOND);
System.out.println(h+":"+m+":"+s);
/*
* 根据当前Calendar表示的日期获取指定时间分量所允许的最大值
* 今年总共多少天?
*/
int days = calendar.getActualMaximum(Calendar.DAY_OF_YEAR);
System.out.println("今年共:"+days+"天");
}
}
# Calendar_set
package date;
import java.util.Calendar;
/**
* Calendar提供了一个set方法,可以对指定时间分量设置对应的值:
* @author chengyi
*
*/
public class Calendar_set {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.getTime());
/*
* 设置Calendar为2008-08-08 20:08:08
*/
//设置年
calendar.set(Calendar.YEAR, 2008);
//设置月
calendar.set(Calendar.MONTH, Calendar.AUGUST);
//设置日
calendar.set(Calendar.DAY_OF_MONTH, 8);
System.out.println(calendar.getTime());
//设置小时
calendar.set(Calendar.HOUR_OF_DAY, 20);
calendar.set(Calendar.MINUTE,8);
calendar.set(Calendar.SECOND, 8);
System.out.println(calendar.getTime());
/*
* 设置为周4
* 按照西方的标准,一周的第一天为周日
*/
calendar.set(Calendar.DAY_OF_WEEK, 5);
System.out.println(calendar.getTime());
}
}
# Calendar_add
package date;
import java.util.Calendar;
/**
* Calendar提供了计算时间的方法:
* void add(int field,int amount)
* 对给定的时间分量加上指定的值,若给定的值为负数,则是减去。
*/
public class Calendar_add {
public static void main(String[] args){
Calendar calendar = Calendar.getInstance();
/*
* 查看3年2个月零25天以后是哪天?
*/
//加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, 3);
System.out.println(calendar.getTime());
}
}
```
## lambda
### 表达式
```java
package lambda;
/**
* JDK8之后推出了一个新的特性:lambda表达式
* 使用lambda可以快速简单的创建仅含有一个抽象方法的接口
* 或抽象类的子类实例(替代匿名内部类创建)
*
* lambda语法:
* ([参数列表])->{方法体}
* @author chengyi
*
*/
public class LambdaDemo1 {
public static void main(String[] args) {
Runnable r = new Runnable(){
public void run(){
System.out.println("匿名内部类");
}
};
Thread t = new Thread(r);
t.start();
/*
* lambda是编译器认可的,最终会改为内部类形式创建并且编译器
* 会结合源代码上下文自动分析要匿名创建的类型。由于要求该接口只能有
* 一个方法,所以方法不存在争议。
*/
Runnable r1 = ()->{
System.out.println("lambda");
};
/*
* 当重写的方法中只有一句代码时,lambda可以忽略{}
*/
Runnable r3 = ()->System.out.println("省略大括号的lambda");
System.out.println("main方法运行完毕");
}
}
# 重写有参数的抽象方法
package lambda;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* 重写有参数的抽象方法
* @author chengyi
*
*/
public class LambdaDemo2 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("常老师");
list.add("传奇");
list.add("小泽老师");
Collections.sort(list,new Comparator<String>(){
public int compare(String o1,String o2){
return o1.length()-o2.length();
}
});
System.out.println(list);
/*
* 编译器会结合上下文分析参数类型,若不能确定时编译器会要求指定参数类型。
* 若可以省略{}时,方法若需要返回值,那么return关键字也需要一同省略。
*/
Collections.sort(list,(o1,o2)->o1.length()-o2.length());
}
}
# Map
Map 翻译为 映射
- Map是接口,在java.util包
- Java中提供的面向“查询”的API
- 查询中输入的被检索信息称为“key”,key翻译为关键信息
- 查询结构称为“value”,value翻译为“值”
- map提供了根据key查询Value的功能
- Map接口有两个常用实现类
- 最重要的实现类 HashMap,Hash 散列, HashMap也称为散列表,哈希表
- HashMap是查询最快的API
- TreeMap 采用二叉树算法。相当于2分查找,速度很快。
- Map中Key不允许重复,Value可以重复
- 尽量将软件中的查找功能用map进行优化!提高软件的性能
- map集合是无序的!
# 使用Map
- 创建map对象,是空的,没有数据。
Map map = new HashMap();
- 被查询的Key-Value 成对的存储到Map
Object obj = map.put(key, value); 如果第一次put,返回null,如果第二次添加同样的key,返回被替换的对象
- 查询时候,根据Key查询到Value
value = map.get(key); 如果没有找到返回null
public static void main(String[] args) {
/*
* 将数据添加到Map中
*/
Map map = new HashMap();
//第一次将“u1”-“Tom”添加到Map
Object obj = map.put("u1", "Tom"); //向上造型
//第一次返回null
System.out.println(obj);
//第二次将“u1”的value替换为jerry
obj = map.put("u1", "Jerry");
//返回被替换的Tom
System.out.println(obj);
//显示map中的内容,思考如下输出用了那个方法?
//答:利用的是HashMap重写的toString
System.out.println(map);//{u1=Jerry}
/*
* 利用泛型定义类型安全的map集合
*/
Map<String,String> map2 = new HashMap<>();//java1.8开始后面的泛型可以省略
String s = map2.put("u1", "tom");
System.out.println(s);
s = map2.put("u1", "Jerry");
System.out.println(s);
System.out.println(map2);
}
public static void main(String[] args) {
/*
* 测试map的查询方法
*/
Map<String,String> map = new HashMap<>();
//向map中添加数据
map.put("u1", "tom");
map.put("u5","Jerry");
map.put("u99", "chengyi");
map.put("u20","awngkejing");
System.out.println(map);
//从map中检索
String v1 = map.get("u1");
String v2 = map.get("u3");
System.out.println(v1);
System.out.println(v2);//null 为什么不报空指针异常?
/*
* get出来的元素并不是按照指针顺序读写的,不是关于指针的操作,没找到就是没找到
*/
}
# Map的API方法
- size() 检测map集合中元素的个数
- isEmpty() 检测map集合是否为空
- remove() 删除map集合中的指定元素,返回被删除的value
- clear() 清空map集合
- containsKey("u2") 检测集合是不是包含指定的key
public static void main(String[] args) {
/*
* 演示Map的方法
* size()集合中元素的数量
* isEmpty() 检测集合是否为空
* remove() 删除集合中的元素
*/
Map<String , String> map = new HashMap<>();
System.out.println(map.size());
System.out.println(map.isEmpty());
map.put("u1", "Tom");
map.put("u1", "Jerry");
map.put("u3", "Andy");
map.put("u4","iohn");
System.out.println(map.size());
System.out.println(map.isEmpty());
//从map中删除u1元素,返回被删除的value
String val = map.remove("u1");
System.out.println(map.size());
System.out.println(map.isEmpty());
map.clear();
System.out.println(map.size());
}
public static void main(String[] args) {
/*
* 测试cotainsKey方法
* 检测集合中是否包含指定的key
*/
Map<String,String> map = new HashMap<>();
map.put("u1", "tom");
map.put("u2", "Jerry");
map.put("u3", "Andy");
map.put("u4", "Mac");
//map中允许添加一个key为null的数据
map.put(null, "wang");
//map集合无序!
System.out.println(map);
//检测map集合是否包含指定的key
boolean b1 = map.containsKey("u1");
boolean b2 = map.containsKey("u5");
boolean b3 = map.containsKey(null);
System.out.println(b1);//true
System.out.println(b2);//false
System.out.println(b3);//true
Map<String,String> map1 = new HashMap<>();
map1.put("u4", "chengyi");
map1.put("u5","wangkejing");
map1.put("u6","fanchuanqi");
System.out.println(map1);
//如果key相同,则会进行覆盖
map.putAll(map1);
System.out.println(map);
//{null=wang, u5=wangkejing, u6=fanchuanqi, u1=tom, u2=Jerry, u3=Andy, u4=chengyi}
System.out.println(map1);//{u5=wangkejing, u6=fanchuanqi, u4=chengyi}
}
# HaskMap 的工作原理(有图)
- HashMap内部利用数组存储数据。
- 根据key的hashCode值计算数组的下标位置,进行添加数据或查询数据。 -根据hashCode计算出数组下标位置的算法称为“散列算法”
- 数组下标位置会重复,重复时候利用链表存储重复元素 -这个链表称为“散列桶”
- 添加和查询时候如果有散列桶,则根据equals方法逐个比较找到位置。
由于利用hashCode直接定位到数组的存储位置,无需依次查找,所以 HashMap具有高查找性能。
- 影响HashMap的查找性能因素:
- 如果数据多,而数组容量少,大量数据重复的存储在散列桶中,造成在散列桶中 进行大量的舒徐查找,性能差。
- 解决办法是提供更多的数组容量,减少散列桶问题
- 如果保持 元素的总数和数组容量的比值小于75%时候,出现重复位置的情况少于3个!
- HashMap中默认的“加载因子”值就是75%,HashMap中添加元素时候,HashMap始终保持元素和数组容量的比值小于75%,如果超过75%则进行数组扩容“重新散列”
- hashCode 方法
- java在Object类上定义了hashCode方法,用于支持HashMap中的算法
- 作为key的类型必须很好的实现hashCode方法没否则会影响hashMap性能
- 当两个对象 equals方法比较结果为true时候,他们的hashCode相同
- 当两个对象 equals方法比较结果为false时候,他们的hashCode近可能不同!
- qeuals方法
- HashMap添加或查找时候,先根据hashCode计算数组下标位置,然后再利用equals比较key对象是否相同
- 如果key的hashCode和equals方法不“一致”,会造成HashMap工作异常可能重复添加或者查找不到数据
建议:一定成对重写key的equals方法和hashCode方法 Java中的API String, Integer等都成对重写了equals和hashCode.
public class MapHashCodeDemo {
public static void main(String[] args) {
/*
* 作为key的类,如果不很好的重写equals和hashCode方法会造成HashMap
* 工作故障
*/
HashMap<Player,String> map = new HashMap<>();
//不太明白这里直接传对象内存怎么走
map.put(new Player(1), "吃鸡");
map.put(new Player(2), "英雄联盟");
map.put(new Player(3), "英雄联盟");
Player one = new Player(1);
String game = map.get(one);
System.out.println(game);
}
}
class Player{
int id;
public Player(int id){
this.id = id;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Player other = (Player) obj;
if (id != other.id)
return false;
return true;
}
}
- 创建HashMap的性能优化参数
- new HashMap(数组容量,加载因子)
- 默认 new HashMap() 等价于 new HashMap(16, 0.75f)
- 在添加到12个元素以后进行扩容
- 如果事先可以预测添加到HashMap中数量,则可以声明足够的大的容量,避免反复扩容浪费时间。
- 如果有1000条数据需要添加到HashMap,则new HashMap(1500)
# Map的遍历
package map;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
public class MapEnrtySetDemo {
public static void main(String[] args) {
/*
* 利用EntrySet对map集合进行遍历
*/
//如下数据相当于从浏览器收到的请求头
map.put("Host", "doc.tedu.cn");
map.put("Connection", "keep-alive");
map.put("CaChe-Contro1", "max-age=0");
//...
//遍历全部的请求头
//map没有提供直接遍历的方法!
//可以利用EntrySet 和 KeySet 间接实现遍历
//Entry对象就代表map中的key-value对
//一个Entry对象中包含两个属性,一个是key
//一个是Value, Entry的实现类是HashMap内部类 ***划重点
Set<Entry<String,String>> set = map.entrySet();
//set中包含map中全部的key-value对
//只要遍历set就相当于遍历了map
for(Entry<String,String> e : set){
System.out.println(e.getKey()+","+e.getValue());
}
}
}
# reflect反射
# 概念
package reflect;
/**
* java反射机制
* 反射机制允许我们对一个类的加载,实例化,调用方法,操作属性从编码期
* 改为运行期进行。这大大的提高了代码的灵活度。
* 但是运行期进行反射操作会消耗额外的资源与性能。所以要适度使用。
*
* @author chengyi
*
*/
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
/*
* JVM加载一个类有以下几种方式:
* 1:执行代码时,发现需要用到某个类时
* 如:Person p = new Person()
* 这时候JVM会加载Person.class
*
* 2:通过反射机制中的:
* Class.forName(String className)
* 方法以字符串形式告知JVM加载指定的类
* 此方法只能寻找环境变量中配置的类路径中指定的类
*
* 3:通过类加载器ClassLoader来加载指定的类,
* 类加载器可以额外指定环境变量中没有指定的类路径,
* 并从中寻找指定的类进行加载
* 除第一种方式外,剩下两种都是基于反射机制动态加载一个类。
*/
//JVM首先会加载Person.class文件
Person p = null;
p = new Person();
/*
* 加载类的过程就是让JVM读取该类对应的class文件
*
* 当JVM读取一个类的class文件后,会实例化一个Class类的实例用于保存加载的这个类
* 的信息。并且每个被加载的类只会进行一次加载过程,这意味着每个被JVM加载的类都会
* 在JVM内部以一个Class类的实例进行保存。所以每个类有且只有一个Class类的实例与
* 之对应。
* Class也称为类对象。每个实例用于表示JVM加载的一个类,通过它可以获取其表示的
* 类的相关信息。
* 比如:类的名字,有那些属性,构造器,以及所有方法。并且通过Class还可以实例化其
* 表示的类。
*/
Class cls =Class.forName("reflect.Person");
/*
* Class提供了一个方法:
* Object newInstance()
* 该方法是调用当前Class所表示的类的无参构造方法,实例化一个该类实例并将其返回。
* 注意,该类必须有无参构造方法才可以使用这个方法。
*/
Object obj = cls.newInstance();
System.out.println(obj);
}
}
# 调用方法
package reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 利用反射机制调用某个类实例的方法
* @author chengyi
*
*/
public class Demo3 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException{
//1 实例化
Person p = new Person();
//2 调用该对象的方法
p.sayHello();
Class cls = Class.forName("reflect.Person");
Object o = cls.newInstance();
//获取Person类的sayHello()方法
/*
* Class提供了获取其表示的类相关信息的一组方法,
* 其中:
* Method getDeclaredMethod(String name,Class[] arrays);
* 是获取当前Class所表示的类定义的指定名字及参数列表的方法。
*
* Method是反射API中一个重要的类。
* 其每一个实例用于表示某个类的一个具体方法。
* 通过Method可以获取到其表示的方法的相关信息,
* 如:方法名,返回值类型,访问修饰符等。
* 并且也可以通过Method动态调用其表示的方法。
*/
Method method = cls.getDeclaredMethod("sayHello", null);
/*
* Method的invoke方法是用来调用当前Method所表示的方法。
* 参数1:由于成员方法所属对象,那么调用Method所表示的方法时
* 要传入该方法所属对象
* 参数2:实际参数,若该方法无参,则传入null即可
*
* 即:
* 若method表示的是Person类的方法sayHello
* 那么
* method.invoke(o,null);
* 等同于
* o.sayHello();
*/
method.invoke(o, null);
}
}
# 调用带参方法
package reflect;
import java.lang.reflect.Method;
/**
* 调用含有参数的方法
* @author chengyi
*
*/
public class Demo4 {
public static void main(String[] args) throws Exception{
Person p = new Person();
p.sayHello("程意",23);
//1加载Person
Class cls = Class.forName("reflect.Person");
//2实例化
Object o = cls.newInstance();
//3获取sayHello(String name,int age)
Method method = cls.getDeclaredMethod("sayHello", new Class[]{String.class, int.class });
//4 p.sayHello("程意",23);
method.invoke(o, new Object[]{"程意",23});
}
}
# 调用私有成员
package reflect;
import java.lang.reflect.Method;
/**
* 反射可以调用一个类的私有成员
* @author chengyi
*
*/
public class Demo5 {
public static void main(String[] args) throws Exception{
// Person p = new Person();
// p.dosome();//编译错误,私有方法不可见
Class cls = Class.forName("reflect.Person");
Object o = cls.newInstance();
Method method = cls.getDeclaredMethod("dosome",null);
//若要调用私有方法,需要设置访问权限
method.setAccessible(true);
method.invoke(o, null);
}
}
// 用到的person类
package reflect;
/**
* 使用该类测试反射机制
* @author chengyi
*
*/
public class Person {
public void sayHello(){
System.out.println("大家好!");
}
public void sayHello(String name){
System.out.println("大家好!我叫"+name);
}
public void sayHello(String name, int age){
System.out.println("大家好!我叫"+name+",今年"+age);
}
public void sayHi(){
System.out.println("Hi!");
}
private void dosome(){
System.out.println("我是私有的!");
}
}