1.接收输入
import java.util.Scanner;public class Scann {public static void main(String[] args) {System.out.println("等待输入");Scanner sc = new Scanner(System.in);int num = sc.nextInt();System.out.println(num);}
}
2.数组
初始化声明
public class arrTest {public static void main(String[] args) {//一维int[] ids;//声明//1.1 静态初始化:数组的初始化和数组元素的赋值操作同时进行ids = new int[]{1001, 1002, 1003, 1004};//1.2动态初始化:数组的初始化和数组元素的赋值操作分开进行String[] names = new String[5];//可以是变量int num = 10;int[] nums = new int[num];//二维//静态初始化int[][] arr1 = new int[][]{{1, 2, 3}, {4, 5}, {6, 7, 8}};//动态初始化1String[][] arr2 = new String[3][2];//动态初始化2String[][] arr3 = new String[3][];//也是正确的写法:int[] arr4[] = new int[][]{{1, 2, 3}, {4, 5, 9, 10}, {6, 7, 8}};}
}
数组默认值
一维
- 数组元素是整型:0
- 数组元素是浮点型:0.0
- 数组元素是char型:0或’\u0000’,而非’0’
- 数组元素是boolean型:false
- 数组元素是引用数据类型:null
二维
-
规定:二维数组分为外层数组的元素,内层数组的元素
-
针对于初始化方式一:比如:
int[][] arr = new int[4][3];
-
外层元素的初始化值为:地址值
-
内层元素的初始化值为:与一维数组初始化情况相同
-
针对于初始化方式二:比如:
int[][] arr = new int[4][];
-
外层元素的初始化值为:null
-
内层元素的初始化值为:不能调用,否则报错。
数组常见方法使用
进行导包
import java.util.Arrays;
int arr11[] = new int[]{21, 43, 542, 432, 4, 2, 5, 1};
int arr22[] = new int[]{32, 43, 4, 1, 4, 76, 54, 68, 4};// 1.判断数组是否相等
System.out.println(Arrays.equals(arr11, arr22));
// 2.输出数组信息
System.out.println(Arrays.toString(arr11));// 3.将指定值填充到数组中
Arrays.fill(arr11, 2);
System.out.println(Arrays.toString(arr11));
// 4.对数组进行排序
Arrays.sort(arr22);
System.out.println(Arrays.toString(arr22));
// 5.堆排序好的数组用二分法检索指定值
int index = Arrays.binarySearch(arr22, 1);
System.out.println(index);
3.对象,类
基本使用
注:静态类加载比非静态优先,因此静态类只能调用静态方法
Person
public class Person {String name;int age;public void run(){System.out.println("跑跑跑");}public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
Test
public class Test {public static void main(String[] args) {Person p1 = new Person("rokned", 22);System.out.println(p1.name + " " + p1.age);p1.run();}
}
方法重载
public void run() {System.out.println("跑跑跑");
}//方法重载
public void run(int n) {System.out.println("跑" + n + "圈");
}
可变参数
//可变参数
public void run(int ...ns){for(int temp:ns){System.out.println(temp);}
}
继承
不支持多继承
public class Student extends Person{public Student(String name, int age) {super(name, age);}@Overridepublic void run() {super.run();System.out.println("学生跑");}
}
多态(向上转型)
多态的作用:可以减少重复代码
父类引用指向子类对象
调用方法时,只能调用父类中有的方法,如果子类有重写,则调用重写的。
//多态
Person p3 = new Student("duotai", 21);
p3.run();
// p3.eat();会报错,父类中没有eat方法
向下转型
//向下转型,使用强转
Student p4 = (Student) p3;
p4.eat();//此时可以调用子类独有的方法
instanceof
a instanceof A 判断a是否是A的实例
//instanceof
System.out.println((p4 instanceof Student)); //true
System.out.println((p4 instanceof Person)); //true
System.out.println((p4 instanceof Object)); //true
包装类
int num = 10;
//基本转包装,其他类型同理
Integer in1 = new Integer(num);
Integer in2 = new Integer("123");
Integer in3 = new Integer(123);//包装转基本,其他类型同理
int i1 = in1.intValue();//自动拆装箱
Integer in4 = i1;
int i2 = in4;//字符串转换
int num1 = 10;
String s1 = num1 +"";
int num2 = Integer.parseInt(s1);
System.out.println(num2);
抽象类
子类必须实现抽象父类中的方法。
abstract class EarthPeople {//抽象类不能实例化,一定有构造器public EarthPeople() {}//抽象类可以没有抽象方法,但是有抽象方法的一定是抽象类public abstract void fly();
}
接口
可以多个接口相接,必须实现接口中的方法,使用implements进行实现
public interface gun {//接口不能定义构造器,不可以实例化public void shoot();
}
内部类
和普通类类似,不过定义在另一个类中
可以调用外部类结构,同时静态不能调用非静态。
public class Inner {//静态成员内部类static class AA {}//非静态成员内部类class BB {}
}
调用:
//创建静态类实例对象
Inner.AA aa = new Inner.AA();
//创建非静态类实例对象
// Inner.BB bb = new Inner.BB(); 错误
Inner in = new Inner();
Inner.BB bb = in.new BB();
4.异常
try-catch-finally
try{String str = "123";str = "abc";int num = Integer.parseInt(str);
}
//一个try可以写多个catch
catch (NumberFormatException e){System.out.println("出错了");
}
catch (Exception e){System.out.println("出大错了");
}
finally {//可选,一定会执行的代码
}
throws
抛出可能的异常,但是还是会报错
public static void main(String[] args) throws Exception{String str = "123";str = "abc";int num = Integer.parseInt(str);
}
throw
可以手动抛出异常
public static void main(String[] args) throws Exception {throw new Exception("输出异常");
}
5.多线程
创建方式一:继承Thread
一个线程只能执行一个start
public class ThreadTest {public static void main(String[] args) {//3.创建实例MyThread mt = new MyThread();//4.调用startmt.start();for (int i = 0; i <= 100; i++) {System.out.println(Thread.currentThread().getName() + ":" + "-------");}}
}//1.继承thread
class MyThread extends Thread {//2.重写run方法@Overridepublic void run() {for (int i = 0; i <= 20; i++) {System.out.println(Thread.currentThread().getName() + ":" + i);}}
}
创建方式二:实现Runnable接口
public class ThreadTest1 {public static void main(String[] args) {MyThread1 myThread1 = new MyThread1();Thread t1 = new Thread(myThread1);t1.start();for (int i = 0; i <= 100; i++) {System.out.println(Thread.currentThread().getName() + ":" + "-------");}}
}class MyThread1 implements Runnable {@Overridepublic void run() {for (int i = 0; i <= 20; i++) {System.out.println(Thread.currentThread().getName() + ":" + i);}}
}
创建方式三:Callable接口
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class CallableTest {public static void main(String[] args) {//3.创建Callable接口实现类的对象NumThread numThread = new NumThread();//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象FutureTask futureTask = new FutureTask(numThread);//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()new Thread(futureTask).start();try {//6.获取Callable中call方法的返回值//get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。Object sum = futureTask.get();System.out.println("总和为:" + sum);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}}//1.创建一个实现Callable的实现类
class NumThread implements Callable {//2.实现call方法,将此线程需要执行的操作声明在call()中@Overridepublic Object call() throws Exception {int sum = 0;for (int i = 1; i <= 100; i++) {if (i % 2 == 0) {System.out.println(i);sum += i;}}return sum;}
}
创建方式四:线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;class NumberThread implements Runnable{@Overridepublic void run() {for(int i = 0;i <= 100;i++){if(i % 2 == 0){System.out.println(Thread.currentThread().getName() + ": " + i);}}}
}class NumberThread1 implements Runnable{@Overridepublic void run() {for(int i = 0;i <= 100;i++){if(i % 2 != 0){System.out.println(Thread.currentThread().getName() + ": " + i);}}}
}public class ThreadPool {public static void main(String[] args) {//1. 提供指定线程数量的线程池ExecutorService service = Executors.newFixedThreadPool(10);ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;//设置线程池的属性// System.out.println(service.getClass());// service1.setCorePoolSize(15);// service1.setKeepAliveTime();//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象service.execute(new NumberThread());//适合适用于Runnableservice.execute(new NumberThread1());//适合适用于Runnable// service.submit(Callable callable);//适合使用于Callable//3.关闭连接池service.shutdown();}
}
线程安全
同步代码块
使用同步代码块,防止线程同时操作一个数据
优点:解决了安全问题,缺点:操作同步代码时,只能有一个线程参与,相当于单线程过程,效率低
synchronized (this),runnable可以使用this,thread不能使用this,可以使用反射,也可以定义一个静态对象
括号内是一把锁,可以是任意对象。但一定只能有一把锁,不然没有作用
public class Ticket implements Runnable {private int tick = 100;@Overridepublic void run() {while (true) {synchronized (this) {if (tick > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "号窗口买票,票号为:" + tick--);} else {break;}}}}
}class TicketTest {public static void main(String[] args) {Ticket ticket = new Ticket();Thread thread1 = new Thread(ticket);Thread thread2 = new Thread(ticket);Thread thread3 = new Thread(ticket);thread1.setName("窗口1");thread2.setName("窗口2");thread3.setName("窗口3");thread1.start();thread2.start();thread3.start();}
}
同步方法
public class Ticket3 implements Runnable {private int tick = 100;private boolean isFlag = true;@Overridepublic void run() {while (isFlag) {show();}}public synchronized void show() {//同步show方法,继承Thread类方法一样,只需同步方法即可,同时需要给方法加static关键字,确保不会创建多个对象if (tick > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "号窗口买票,票号为:" + tick--);} else {isFlag = false;}}
}class TicketTest3 {public static void main(String[] args) {Ticket3 ticket = new Ticket3();Thread thread1 = new Thread(ticket);Thread thread2 = new Thread(ticket);Thread thread3 = new Thread(ticket);thread1.setName("窗口1");thread2.setName("窗口2");thread3.setName("窗口3");thread1.start();thread2.start();thread3.start();}
}
Lock锁
import java.util.concurrent.locks.ReentrantLock;class Window implements Runnable{private int ticket = 100;//1.实例化ReentrantLockprivate ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {while(true){try{//2.调用锁定方法lock()lock.lock();if(ticket > 0){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":售票,票号为:" + ticket);ticket--;}else{break;}}finally {//3.调用解锁方法:unlock()lock.unlock();}}}
}public class LockTest {public static void main(String[] args) {Window w = new Window();Thread t1 = new Thread(w);Thread t2 = new Thread(w);Thread t3 = new Thread(w);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}
}
Thread常用方法
优先级设置
wait,notify,notifyall
wait():使线程进入阻塞,并释放同步监视器(锁)
notify():唤醒一个线程,如果有多个线程wait,则唤醒优先级高的
notifyall():唤醒所有线程
这三个方法必须在同步代码块或同步方法中,调用者必须是其中的同步监视器,否则会报错,他们定义在java.lang.Object类中
6.常用类
字符串
String
不可变性
实例化方法
//通过字面量定义的方式:此时的s1和s2的数据javaEE声明在方法区中的字符串常量池中。
String s1 = "javaEE";
String s2 = "javaEE";
//通过new + 构造器的方式:此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应的地址值。
String s3 = new String("javaEE");
String s4 = new String("javaEE");System.out.println(s1 == s2);//true
System.out.println(s1 == s3);//false
System.out.println(s1 == s4);//false
System.out.println(s3 == s4);//false
拼接对比
- 常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
- 只要其中一个是变量,结果就在堆中。
- 如果拼接的结果调用 intern() 方法,返回值就在常量池中
String s1 = "javaEE";
String s2 = "hadoop";String s3 = "javaEEhadoop";
String s4 = "javaEE" + "hadoop";
String s5 = s1 + "hadoop";
String s6 = "javaEE" + s2;
String s7 = s1 + s2;System.out.println(s3 == s4);//true
System.out.println(s3 == s5);//false
System.out.println(s3 == s6);//false
System.out.println(s3 == s7);//false
System.out.println(s5 == s6);//false
System.out.println(s5 == s7);//false
System.out.println(s6 == s7);//falseString s8 = s6.intern();//返回值得到的s8使用的常量值中已经存在的“javaEEhadoop”
System.out.println(s3 == s8);//true
****************************
String s1 = "javaEEhadoop";
String s2 = "javaEE";
String s3 = s2 + "hadoop";
System.out.println(s1 == s3);//falsefinal String s4 = "javaEE";//s4:常量
String s5 = s4 + "hadoop";
System.out.println(s1 == s5);//true
常用操作
操作
int length():返回字符串的长度: return value.lengthchar charAt(int index): 返回某索引处的字符 return value[index]boolean isEmpty() :判断是否是空字符串: return value.length == 0String toLowerCase() :使用默认语言环境,将 String 中的所字符转换为小写String toUpperCase() :使用默认语言环境,将 String 中的所字符转换为大写String trim() :返回字符串的副本,忽略前导空白和尾部空白boolean equals(Object obj) :比较字符串的内容是否相同boolean equalsIgnoreCase(String anotherString) :与 equals() 方法类似,忽略大小写String concat(String str) :将指定字符串连接到此字符串的结尾。 等价于用 +int compareTo(String anotherString) :比较两个字符串的大小String substring(int beginIndex) :返回一个新的字符串,它是此字符串的从beginIndex 开始截取到最后的一个子字符串。
String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从 beginIndex 开始截取到 endIndex (不包含)的一个子字符串。String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所 oldChar 得到的。
String replace(CharSequence target, CharSequence replacement):使用指定的字符串替换此字符串中出现的目标字符串。String replaceAll(String regex, String replacement):使用给定的 replacement 替换此字符串所匹配给定的正则表达式的子字符串。
String replaceFirst(String regex, String replacement):使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。
String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。
判断
boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
查找
boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列时,返回 trueint indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
注:indexOf 和 lastIndexOf 方法如果未找到都是返回-1
转换
String --> char[]:调用String的 toCharArray()
char[] --> String:调用String的构造器 new String(charArray)编码:String --> byte[]:调用String的 getBytes(),可选不同编码
解码:byte[] --> String:调用String的构造器
说明:解码时,要求解码使用的字符集必须与编码时使用的字符集一致,否则会出现乱码。stringbuffer,stringbuilder是可变的字符序列
stringbuffer线程安全的,效率低,stringbuilder反之
String -->StringBuffer、StringBuilder: 调用StringBuffer、StringBuilder构造器
StringBuffer、StringBuilder -->String:
①调用String构造器; ②StringBuffer、StringBuilder的toString()
StringBuffer和StringBuilder
是可变的字符序列
StringBuilder和 StringBuffer非常类似,均代表可变的字符序列,而且提供相关功能的方法也一样,只是StringBuilder类没有加线程锁,执行效率更高。
构造器
StringBuffer():初始容量为16的字符串缓冲区
StringBuffer(int size):构造指定容量的字符串缓冲区
StringBuffer(String str):将内容初始化为指定字符串内容
常用方法
StringBuffer append(xxx):提供了很多的 append() 方法,用于进行字符串拼接StringBuffer delete(int start,int end):删除指定位置的内容StringBuffer replace(int start, int end, String str):把[start,end)位置替换为strStringBuffer insert(int offset, xxx):在指定位置插入xxxStringBuffer reverse() :把当前字符序列逆转public int indexOf(String str):返回子串的下标public String substring(int start,int end):返回一个从start开始到end索引结束的左闭右开区间的子字符串public int length():获取字符串的长度public char charAt(int n ):返回指定位置的字符public void setCharAt(int n ,char ch):设置指定位置的字符
执行效率
String、StringBuffer、StringBuilder三者的执行效率
从高到低排列:StringBuilder > StringBuffer > String
时间
Date
Date()
:使用无参的构造器创建对象可以获取本地当前时间
@Test
public void dateTest(){//构造器一:Date():创建一个对应当前时间的Date对象Date date1 = new Date();System.out.println(date1.toString());//Sun Apr 19 13:35:12 CST 2020System.out.println(date1.getTime());//1587274512876//构造器二:创建指定毫秒数的Date对象Date date2 = new Date(15872745176L);System.out.println(date2.toString());System.out.println("-----------------------");//创建java.sql.Date对象,对应数据库中日期类型变量java.sql.Date date3 = new java.sql.Date(1587274512876L);System.out.println(date3.toString());//如何将java.util.Date对象转换为java.sql.Date对象Date date4 = new Date();//第一种方式,存在问题:java.util.Date cannot be cast to java.sql.Date// java.sql.Date date6 = (java.sql.Date) date4;// System.out.println(date6);//第二种方式java.sql.Date date5 = new java.sql.Date(date4.getTime());System.out.println(date5);
}
SimpleDateFormat
时间格式化
@Test
public void test2() throws ParseException {//实例化Date对象Date date1 = new Date();//实例化SimpleDateFormate对象,并设置显示格式SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:aaa");//格式化date对象String format = simpleDateFormat.format(date1);System.out.println(format.toString());//2020-09-19 02:09:下午//解析:要求字符串必须是符合SimpleDateFormat识别的格式(通过构造器参数体现),//否则,抛异常Date date2 = simpleDateFormat.parse("2020-04-20 14:20:下午");System.out.println(date2.toString());//Tue Jan 21 02:20:00 CST 2020
}
DateTimeFormatter
@Test
public void test3(){// 方式一:预定义的标准格式。// 如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIMEDateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;//格式化:日期-->字符串LocalDateTime localDateTime = LocalDateTime.now();String str1 = formatter.format(localDateTime);System.out.println(localDateTime);//2020-04-21T19:13:13.530System.out.println(str1);//2020-04-21T19:13:13.53//解析:字符串 -->日期TemporalAccessor parse = formatter.parse("2000-04-21T19:13:13.53");System.out.println(parse);//{},ISO resolved to 2000-04-21T19:13:13.530// 方式二:// 本地化相关的格式。如:ofLocalizedDateTime()// FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT :适用于LocalDateTimeDateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);//格式化String str2 = formatter1.format(localDateTime);System.out.println(str2);//2020年4月21日 下午07时16分57秒// 本地化相关的格式。如:ofLocalizedDate()// FormatStyle.FULL / FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT : 适用于LocalDateDateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);//格式化String str3 = formatter2.format(LocalDate.now());System.out.println(str3);//2020-4-21// 重点: 方式三:自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”)DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");String Str4 = formatter3.format(LocalDateTime.now());System.out.println(Str4);//2020-04-21 07:24:04TemporalAccessor accessor = formatter3.parse("2020-02-03 05:23:06");System.out.println(accessor);//{SecondOfMinute=6, HourOfAmPm=5, NanoOfSecond=0, MicroOfSecond=0, MinuteOfHour=23, MilliOfSecond=0},ISO resolved to 2020-02-03
}
Calendar
日历类
注意: 获取月份时:一月是0,二月是1,以此类推,12月是11 获取星期时:周日是1,周二是2,。。。周六是7
Calendar calendar = Calendar.getInstance();
// System.out.println(calendar.getClass());//2.常用方法
//get()
int days = calendar.get(Calendar.DAY_OF_MONTH);//获取本月第几天
System.out.println(days);
System.out.println(calendar.get(Calendar.DAY_OF_YEAR));//获取本年第几天//set()
//calendar可变性
calendar.set(Calendar.DAY_OF_MONTH,22);//设置本月第几天
days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days);//add()
calendar.add(Calendar.DAY_OF_MONTH,-3);
days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days);//getTime():日历类---> Date
Date date = calendar.getTime();
System.out.println(date);//setTime():Date ---> 日历类
Date date1 = new Date();
calendar.setTime(date1);
days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days);
LocalDate / LocalTime / LocalDateTime
@Test
public void test1(){//now():获取当前的日期、时间、日期时间LocalDate localDate = LocalDate.now();LocalTime localTime = LocalTime.now();LocalDateTime localDateTime = LocalDateTime.now();System.out.println(localDate);//2020-04-21System.out.println(localTime);//18:52:54.929System.out.println(localDateTime);//2020-04-21T18:52:54.929//of():设置指定的年、月、日、时、分、秒。没有偏移量LocalDateTime localDateTime1 = LocalDateTime.of(2020,10,6,12,13,12);System.out.println(localDateTime1);//2020-10-06T12:13:12//getXxx():获取相关的属性System.out.println(localDateTime.getDayOfMonth());//21System.out.println(localDateTime.getDayOfWeek());//TUESDAYSystem.out.println(localDateTime.getMonth());//APRILSystem.out.println(localDateTime.getMonthValue());//4System.out.println(localDateTime.getMinute());//52//体现不可变性//withXxx():设置相关的属性LocalDate localDate1 = localDate.withDayOfMonth(22);System.out.println(localDate);//2020-04-21System.out.println(localDate1);//2020-04-22LocalDateTime localDateTime2 = localDateTime.withHour(4);System.out.println(localDateTime);//2020-04-21T18:59:17.484System.out.println(localDateTime2);//2020-04-21T04:59:17.484//不可变性LocalDateTime localDateTime3 = localDateTime.plusMonths(3);System.out.println(localDateTime);//2020-04-21T18:59:17.484System.out.println(localDateTime3);//2020-07-21T18:59:17.484LocalDateTime localDateTime4 = localDateTime.minusDays(6);System.out.println(localDateTime);//2020-04-21T18:59:17.484System.out.println(localDateTime4);//2020-04-15T18:59:17.484
}
Instant时间点
@Test
public void test2(){//now():获取本初子午线对应的标准时间Instant instant = Instant.now();System.out.println(instant);//2020-04-21T11:03:21.469Z//添加时间的偏移量OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));System.out.println(offsetDateTime);//2020-04-21T19:03:21.469+08:00//toEpochMilli():获取自1970年1月1日0时0分0秒(UTC)开始的毫秒数 ---> Date类的getTime()long milli = instant.toEpochMilli();System.out.println(milli);//1587467105795//ofEpochMilli():通过给定的毫秒数,获取Instant实例 -->Date(long millis)Instant instant1 = Instant.ofEpochMilli(1587467105795L);System.out.println(instant1);//2020-04-21T11:05:05.795Z
}
比较器
自然排序
实现Comparable接口,重写compareTo方法
public class Goods implements Comparable{private String name;private double price;//指明商品比较大小的方式:照价格从低到高排序,再照产品名称从高到低排序@Overridepublic int compareTo(Object o) {// System.out.println("**************");if(o instanceof Goods){Goods goods = (Goods)o;//方式一:if(this.price > goods.price){return 1;}else if(this.price < goods.price){return -1;}else{// return 0;return this.name.compareTo(goods.name);}//方式二:// return Double.compare(this.price,goods.price);}// return 0;throw new RuntimeException("传入的数据类型不一致!");}// getter、setter、toString()、构造器:省略
}
定制排序
创建Comparator对象,重写compare方法
arrays.sort(arr,new Comparator(){...})
Comparator com = new Comparator() {//指明商品比较大小的方式:照产品名称从低到高排序,再照价格从高到低排序@Overridepublic int compare(Object o1, Object o2) {if(o1 instanceof Goods && o2 instanceof Goods){Goods g1 = (Goods)o1;Goods g2 = (Goods)o2;if(g1.getName().equals(g2.getName())){return -Double.compare(g1.getPrice(),g2.getPrice());}else{return g1.getName().compareTo(g2.getName());}}throw new RuntimeException("输入的数据类型不一致");}
}
System
Math
枚举类
定义
//使用enum关键字枚举类
enum Season1 {//1.提供当前枚举类的对象,多个对象之间用","隔开,末尾对象";"结束SPRING("春天","春暖花开"),SUMMER("夏天","夏日炎炎"),AUTUMN("秋天","秋高气爽"),WINTER("冬天","冰天雪地");//2.声明Season对象的属性:private final修饰private final String seasonName;private final String seasonDesc;//2.私化类的构造器,并给对象属性赋值private Season1(String seasonName,String seasonDesc){this.seasonName = seasonName;this.seasonDesc = seasonDesc;}//4.其他诉求1:获取枚举类对象的属性public String getSeasonName() {return seasonName;}public String getSeasonDesc() {return seasonDesc;}}
常用方法
Season1 summer = Season1.SUMMER;
//toString():返回枚举类对象的名称
System.out.println(summer.toString());System.out.println("****************");
//values():返回所的枚举类对象构成的数组
Season1[] values = Season1.values();
for(int i = 0;i < values.length;i++){System.out.println(values[i]);
}
System.out.println("****************");
Thread.State[] values1 = Thread.State.values();
for (int i = 0; i < values1.length; i++) {System.out.println(values1[i]);
}//valueOf(String objName):返回枚举类中对象名是objName的对象。
Season1 winter = Season1.valueOf("WINTER");
//如果没objName的枚举类对象,则抛异常:IllegalArgumentException
// Season1 winter = Season1.valueOf("WINTER1");
System.out.println(winter);
实现接口
interface Info{void show();
}//使用enum关键字枚举类
enum Season1 implements Info{//1.提供当前枚举类的对象,多个对象之间用","隔开,末尾对象";"结束SPRING("春天","春暖花开"){@Overridepublic void show() {System.out.println("春天在哪里?");}},SUMMER("夏天","夏日炎炎"){@Overridepublic void show() {System.out.println("宁夏");}},AUTUMN("秋天","秋高气爽"){@Overridepublic void show() {System.out.println("秋天不回来");}},WINTER("冬天","冰天雪地"){@Overridepublic void show() {System.out.println("大约在冬季");}};
}
7.注解
框架 = 注解 + 反射机制 + 设计模式
自定义注解
注解需要配上反射才有意义
public @interface MyAnnotation {String value() default "wuhu";
}
元注解
修饰注解的注解
在这里插入图片描述
可重复注解:
在注解上声明@Repeatable
8.集合
集合的分类
集合框架
|----Collection接口:单列集合,用来存储一个一个的对象|----List接口:存储有序的、可重复的数据。 -->“动态”数组|----ArrayList:作为List接口的主要实现类,线程不安全的,效率高;底层采用Object[] elementData数组存储|----LinkedList:对于频繁的插入删除操作,使用此类效率比ArrayList效率高底层采用双向链表存储|----Vector:作为List的古老实现类,线程安全的,效率低;底层采用Object[]数组存储|----Set接口:存储无序的、不可重复的数据 -->数学概念上的“集合”|----HashSet:作为Set接口主要实现类;线程不安全;可以存null值|----LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加顺序遍历;对于频繁的遍历操作,LinkedHashSet效率高于HashSet.|----TreeSet:可以按照添加对象的指定属性,进行排序。|----Map:双列数据,存储key-value对的数据 ---类似于高中的函数:y = f(x)|----HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value|----LinkedHashMap:保证在遍历map元素时,可以照添加的顺序实现遍历。原因:在原的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。对于频繁的遍历操作,此类执行效率高于HashMap。|----TreeMap:保证照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序底层使用红黑树|----Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value|----Properties:常用来处理配置文件。key和value都是String类型
Collection
常用方法
注:元素中equals方法基本要重写
添加
add(Object obj)
addAll(Collection coll) //将集合元素添加到集合中获取有效元素个数
int size()清空集合
void clear()是否为空集合
boolean isEmpty()是否包含某个元素
boolean contains(Object obj):是通过元素的equals方法来判断是否是同一个对象
boolean containsAll(Collection c):也是调用元素的equals方法来比较的。用两个两个集合的元素逐一比较删除
boolean remove(Object obj):通过元素的equals方法判断是否是要删除的那个元素。只会删除找到的第一个元素
boolean removeAll(Collection coll):取当前集合的差集取两个集合的交集
boolean retainAll(Collection c):把交集的结果存在当前的集合中,不影响c集合是否相等
boolean equals(Object obj)转换成对象数组
Object [] toArray()
集合数组转换
//集合 --->数组:toArray()
Object[] arr = coll.toArray();
for(int i = 0;i < arr.length;i++){System.out.println(arr[i]);
}//拓展:数组 --->集合:调用Arrays类的静态方法asList(T ... t)
List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
System.out.println(list);List arr1 = Arrays.asList(new int[]{123, 456});
System.out.println(arr1.size());//1,将可变参数作为一个整体,输出有问题List arr2 = Arrays.asList(new Integer[]{123, 456});
System.out.println(arr2.size());//2,分别作为两个,输出没问题
遍历
方式1:迭代器
Iterator iterator = coll.iterator();
//hasNext():判断是否还下一个元素
while(iterator.hasNext()){//next():①指针下移 ②将下移以后集合位置上的元素返回System.out.println(iterator.next());
}
//iterator.remove()
@Test
public void test3(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add("Tom");coll.add(false);//删除集合中"Tom"Iterator iterator = coll.iterator();while (iterator.hasNext()){// iterator.remove();Object obj = iterator.next();if("Tom".equals(obj)){iterator.remove();// iterator.remove();}}//将指针重新放到头部,遍历集合iterator = coll.iterator();while (iterator.hasNext()){System.out.println(iterator.next());}
}
方式2:foreach
@Test
public void test1(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//for(集合元素的类型 局部变量 : 集合对象)for(Object obj : coll){System.out.println(obj);}
}
List
存储序有序的、可重复的数据。
基本方法
void add(int index, Object ele):在index位置插入ele元素boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来Object get(int index):获取指定index位置的元素int indexOf(Object obj):返回obj在集合中首次出现的位置int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置Object remove(int index):移除指定index位置的元素,并返回此元素Object set(int index, Object ele):设置指定index位置的元素为eleList subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
ArrayList
@Test
public void test1() {Collection coll = new ArrayList();coll.add(123);coll.add(345);coll.add(new User("Tom", 34));coll.add(new User("Tom"));coll.add(false);//iterator()遍历ArrayList集合Iterator iterator = coll.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}
}
linkedList
- 对与对于频繁的插入和删除元素操作,建议使用LinkedList类,效率更高
- 双向链表
//新增方法
void addFirst(Object obj)
void addLast(Object obj)
Object getFirst()
Object getlast()
Object removeFirst()
Object removeLast()
Set
用于存放无序的、不可重复的元素
Set接口是Collection的子接口,set接口没有提供额外的方法
存储的数据在底层数组中并非照数组索引的顺序添加,而是根据数据的哈希值决定的。
HashSet
-
Hashset是Set接口的典型实现,大多数时候使用Set集合时都使用这个实现类。
-
HashSet具有以下特点:
- 不能保证元素的排列顺序
- HashSet不是线程安全的
- 集合元素可以是null
- 对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则
LinkedHashSet
- LinkedhashSet是HashSet的子类
- LinkedhashSet根据元素的hashCode值来决定元素的存储位置但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
- LinkedhashSet插入性能略低于HashSet,但在迭代访问Set里的全部元素时有很好的性能。
TreeSet
- Treeset是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。
- TreeSet底层使用红黑树结构存储数据
@Test
public void test1(){Set treeSet = new TreeSet();//如果要定制排序,需要传入TreeSet(xxx)treeSet.add(new User("Tom",34));treeSet.add(new User("Jarry",23));treeSet.add(new User("mars",38));treeSet.add(new User("Jane",56));treeSet.add(new User("Jane",60));treeSet.add(new User("Bruce",58));Iterator iterator = treeSet.iterator();while (iterator.hasNext()){System.out.println(iterator.next());}
}
//新增方法
Object first()Object last()Object lower(object e) //返回小于e的最大值Object higher(object e)//返回大于e的最小值SortedSet subSet(fromElement, toElement)SortedSet headSet(toElement)SortedSet tailSet(fromElement)
Map
- Map中的key用set来存放,不允许重复,即同一个Map对象所对应的类,须重写
hashCode()
和equals()
方法 - 常用 String类作为Map的“键”
- key和value之间存在单向一对一关系,即通过指定的key总能找到唯一的、确定的value
- 一组键值对作为一个entry
- Map接口的常用实现类:HashMap、TreeMap、LinkedHashMap和Properties。其中,HashMap是Map接口使用频率最高的实现类
常用方法
Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
void putAll(Map m):将m中的所有key-value对存放到当前map中Object remove(Object key):移除指定key的key-value对,并返回valuevoid clear():清空当前map中的所有数据Object get(Object key):获取指定key对应的valueboolean containsKey(Object key):是否包含指定的key
boolean containsValue(Object value):是否包含指定的valueint size():返回map中key-value对的个数boolean isEmpty():判断当前map是否为空boolean equals(Object obj):判断当前map和参数对象obj是否相等Set keySet():返回所有key构成的Set集合Collection values():返回所有value构成的Collection集合Set entrySet():返回所有key-value对构成的Set集合
HashMap
- HashMap是Map接口使用频率最高的实现类。
- 允许使用null键和null值,与 HashSet一样,不保证映射的顺序。
- 所有的key构成的集合是set:无序的、不可重复的。所以,key所在的类要重写equals()和 hashCode()
@Test
public void test1(){Map map = new HashMap();map.put(null,123);
}
LinkedHashMap
@Test
public void test2(){Map map = new LinkedHashMap();map.put(123,"AA");map.put(345,"BB");map.put(12,"CC");System.out.println(map);
}
TreeMap
- TreeMap存储Key-Value对时,需要根据key-value对进行排序。TreeMap可以保证所有的 Key-Value对处于有序状态。
- TreeSet底层使用红黑树结构存储数据
- TreeMap的Key的排序:
- 自然排序: TreeMap的所有的Key必须实现Comparable接口,而且所有的Key应该是同一个类的对象,否则将会抛出ClasssCastEXception()
- 定制排序:创建 TreeMap时,传入一个 Comparator对象,该对象负责对TreeMap中的所有key进行排序。此时不需要Map的Key实现Comparable接口
Collections工具类
Collections是一个操作Set、List和Map等集合的工具类
reverse(List):反转 List 中元素的顺序shuffle(List):对 List 集合元素进行随机排序sort(List):根据元素的自然顺序对指定 List 集合元素升序排序'
sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素Object min(Collection)
Object min(Collection,Comparator)int frequency(Collection,Object):返回指定集合中指定元素的出现次数void copy(List dest,List src):将src中的内容复制到dest中
//注意创建时需要分配大小
List dest = Arrays.asList(new Object[list.size()]);
Collections.copy(dest,list);boolean replaceAll(List list,Object oldVal,Object newVal)使用新值替换 List 对象的所有旧值
线程安全
//返回的list1即为线程安全的List
List list1 = Collections.synchronizedList(list);
9.泛型
基本使用
- 解决元素存储的安全性问题。
- 解决获取数据元素时,需要类型强制转换的问题。
//在集合中使用泛型,以ArrayList为例
@Test
public void test1(){ArrayList<String> list = new ArrayList<>();list.add("AAA");list.add("BBB");list.add("FFF");list.add("EEE");list.add("CCC");//遍历方式一:Iterator<String> iterator = list.iterator();while (iterator.hasNext()){System.out.println(iterator.next());}System.out.println("-------------");//便利方式二:for (String str:list) {System.out.println(str);}
}
@Test
//在集合中使用泛型的情况:以HashMap为例
public void test2(){Map<String,Integer> map = new HashMap<>();//jdk7新特性:类型推断map.put("Tom",26);map.put("Jarry",30);map.put("Bruce",28);map.put("Davie",60);//嵌套循环Set<Map.Entry<String, Integer>> entries = map.entrySet();Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();while (iterator.hasNext()){Map.Entry<String, Integer> entry = iterator.next();String key = entry.getKey();Integer value = entry.getValue();System.out.println(key+"="+value);}
}
自定义泛型结构
T,K,V,不代表值,而是表示类型。这里使用任意字母都可以。常用T表示,是Type的缩写。
public class Mygeneric<T> {T orderT;public T getOrderT() {return orderT;}public void setOrderT(T orderT) {this.orderT = orderT;}public Mygeneric() {}public Mygeneric(T orderT) {this.orderT = orderT;}
}
public class Mytest {@Testpublic void MyG() {Mygeneric mg = new Mygeneric();mg.setOrderT(123);System.out.println(mg.getOrderT());mg.setOrderT("wh");System.out.println(mg.getOrderT());}
}
注意
异常类不能是泛型的。
如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
泛型的指定中不能使用基本数据类型,可以使用包装类替换。
在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。 (即静态方法中不可以使用本类定义的泛型参数)
/*** 自定义泛型类Order*/
class Order<T> {private String orderName;private int orderId;//使用T类型定义变量private T orderT;public Order() {}//使用T类型定义构造器public Order(String orderName, int orderId, T orderT) {this.orderName = orderName;this.orderId = orderId;this.orderT = orderT;}//这个不是泛型方法public T getOrderT() {return orderT;}//这个不是泛型方法public void setOrderT(T orderT) {this.orderT = orderT;}//这个不是泛型方法@Overridepublic String toString() {return "Order{" +"orderName='" + orderName + '\'' +", orderId=" + orderId +", orderT=" + orderT +'}';}
// //静态方法中不能使用类的泛型。
// public static void show(T orderT){
// System.out.println(orderT);
// }// //try-catch中不能是泛型的。
// public void show(){
// try {
//
// }catch (T t){
//
// }
// }//泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。//换句话说,泛型方法所属的类是不是泛型类都没有关系。//泛型方法,可以声明为静态的。// 原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。//<E>表示是一个泛型,而不是类型,去掉会报错public static <E> List<E> copyFromArryToList(E[] arr) {ArrayList<E> list = new ArrayList<>();for (E e :list) {list.add(e);}return list;}
}
- 子类不保留父类的泛型:按需实现
- 没有类型—擦除
- 具体类型
- 子类保留父类的泛型:泛型子类
- 全部保留
- 部分保留
- 结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型
class Father<T1, T2> {
}/*** 定义泛型子类Son* 情况一:继承泛型父类后不保留父类的泛型*/
//1.没有指明类型 擦除
class Son1<A, B> extends Father {//等价于class Son1 extends Father<Object,Odject>{}
}//2.指定具体类型
class Son2<A, B> extends Father<Integer, String> {
}/*** 定义泛型子类Son* 情况二:继承泛型父类后保留泛型类型*/
//1.全部保留
class Son3<T1, T2, A, B> extends Father<T1, T2> {
}//2.部分保留
class Son4<T2, A, B> extends Father<Integer,T2>{
}
通配符
使用类型通配符:?
比如:List<?>
,Map<?,?>
List<?>
是 List<String>
、List<Object>
等各种泛型 List 的父类。
读取 List<?>
的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object
写入list中的元素时,不可以写入。因为我们不知道写入元素类型,我们不能向其中添加对象。 除了添加null之外。
@Test
public void test3(){List<Object> list1 = null;List<String> list2 = null;List<?> list = null;list = list1;list = list2;//编译通过// print(list1);// print(list2);//List<String> list3 = new ArrayList<>();list3.add("AA");list3.add("BB");list3.add("CC");list = list3;//添加(写入):对于List<?>就不能向其内部添加数据。//除了添加null之外。// list.add("DD");// list.add('?');list.add(null);//获取(读取):允许读取数据,读取的数据类型为Object。Object o = list.get(0);System.out.println(o);
}public void print(List<?> list){Iterator<?> iterator = list.iterator();while(iterator.hasNext()){Object obj = iterator.next();System.out.println(obj);}
}
注意:
//注意点1:编译错误:不能用在泛型方法声明上,返回值类型前面<>不能使用?
public static <?> void test(ArrayList<?> list){
}
//注意点2:编译错误:不能用在泛型类的声明上
class GenericTypeClass<?>{
}
//注意点3:编译错误:不能用在创建对象上,右边属于创建集合对象
ArrayList<> list2 = new ArrayList<?>();
有限制的通配符
-
通配符指定上限
上限
extends
:使用时指定的类型必须是继承某个类,或者实现某个接口,即<=
-
通配符指定下限
下限
super
:使用时指定的类型不能小于操作的类,即>=
@Test
public void test4(){List<? extends Person> list1 = null;List<? super Person> list2 = null;List<Student> list3 = new ArrayList<Student>();List<Person> list4 = new ArrayList<Person>();List<Object> list5 = new ArrayList<Object>();//小于等于Personlist1 = list3;list1 = list4;// list1 = list5;报错//大于等于Person// list2 = list3;报错list2 = list4;list2 = list5;//读取数据:list1 = list3;//要大于等于Person才能接收Person p = list1.get(0);//编译不通过//Student s = list1.get(0);list2 = list4;//只能Object接收Object obj = list2.get(0);编译不通过// Person obj = list2.get(0);//写入数据://编译不通过//无法写入除了null的数据,因为list1中可能有比写入的数据还小的数据// list1.add(new Student());//编译通过//可以写入Person以下的数据list2.add(new Person());list2.add(new Student());}
10.I/O流
File
File基本使用
@Test
public void test1() {//构造器1File file1 = new File("hello.txt");File file2 = new File("E:\\workspace_idea\\JavaSenic\\IO\\hello.txt");System.out.println(file1);System.out.println(file2);//构造器2File file3 = new File("E:\\workspace_idea\\JavaSenior", "hello.txt");System.out.println(file3);//构造器3File file4 = new File("E:\\workspace_idea", "JavaSenior");File file5 = new File(file4, "hi.txt");System.out.println(file4);
}
常用方法
public String getAbsolutePath():获取绝对路径
public String getPath():获取路径
public String getName() :获取名称
public String getParent():获取上层文件目录路径。若无,返回 null
public long length() :获取文件长度(即:字节数)。不能获取目录的长度。
public long lastModified() :获取最后一次的修改时间,毫秒值如下的两个方法适用于文件目录:
public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组
public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组public boolean renameTo(File dest):把文件重命名为指定的文件路径
想保证返回 true ,需要file1在硬盘中是存在的,且file2不能在硬盘中存在。public boolean isDirectory():判断是否是文件目录
public boolean isFile() :判断是否是文件
public boolean exists() :判断是否存在
public boolean canRead() :判断是否可读
public boolean canWrite() :判断是否可写
public boolean isHidden() :判断是否隐藏public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false
public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。
public boolean mkdirs() :创建文件目录。如果此文件目录存在,就不创建了。如果上层文件目录不存在,一并创建public boolean delete():删除文件或者文件夹
删除注意事项:Java中的删除不走回收站。
路径分隔符
windows和DOS系统默认使用 \
来表示
UNIX和URL使用 /
来表示
Java程序支持跨平台运行,因此路径分隔符要慎用。
为了解决这个隐患,File类提供了一个常量: public static final String separator
。根据操作系统,动态的提供分隔符。
//windows和DOS系统
File file1 = new File("E:\\io\\test.txt");
//UNIX和URL
File file = new File("E:/io/test.txt");
//java提供的常量
File file = new File("E:"+File.separator+"io"+File.separator+"test.txt");
流分类
操作数据单位:字节流、字符流
- 对于文本文件(
.txt,.java,.c,.cpp
),使用字符流处理 - 对于非文本文件(
.jpg,.mp3,.mp4,.avi,.doc,.ppt,...
),使用字节流处理
流的角色:节点流、处理流
- 节点流:直接从数据源或目的地读写数据。
- 处理流:不直接连接到数据源或目的地,而是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。
红框为抽象基类,蓝框为常用IO流
节点流(文件流)
字符流读入
@Test
public void testFileReader1() {FileReader fr = null;try {//1.File类的实例化File file = new File("hello.txt");//2.FileReader流的实例化fr = new FileReader(file);//3.读入的操作//方式一,// int data;// while ((data = fr.read()) != -1){// System.out.print((char)data);// }//方式二:read(char[] cbuf):返回每次读入cbuf数组中的字符的个数。如果达到文件末尾,返回-1,相当于几个几个一起读char[] cbuf = new char[5];int len;while ((len = fr.read(cbuf)) != -1) {String str = new String(cbuf, 0, len);System.out.print(str);}} catch (IOException e) {e.printStackTrace();} finally {if (fr != null) {//4.资源的关闭try {fr.close();} catch (IOException e) {e.printStackTrace();}}}
}
字符流写出
@Test
public void Fw() {File file = new File("hello.txt");FileWriter fw = null;try {//如果是true则接在后面写,如果是false则覆盖fw = new FileWriter(file,true);fw.write("\nmy name is wuhu");} catch (IOException e) {e.printStackTrace();} finally {try {fw.close();} catch (IOException e) {e.printStackTrace();}}
}
字节流
//使用字节流FileInputStream处理文本文件,可能出现乱码。
//主要处理非文本文件
@Test
public void testFileInputStream() {FileInputStream fis = null;try {//1. 造文件File file = new File("hello.txt");//2.造流fis = new FileInputStream(file);//3.读数据byte[] buffer = new byte[5];int len;//记录每次读取的字节的个数while((len = fis.read(buffer)) != -1){String str = new String(buffer,0,len);System.out.print(str);}} catch (IOException e) {e.printStackTrace();} finally {if(fis != null){//4.关闭资源try {fis.close();} catch (IOException e) {e.printStackTrace();}}}}
缓冲流
对字节流的包装
- 作用:提供流的读取、写入的速度
- 提高读写速度的原因:内部提供了一个缓冲区。默认情况下是8kb
@Test
public void testBufferedStream(){BufferedInputStream bis = null;BufferedOutputStream bos = null;try {//1.造文件File srcFile = new File("test.jpg");File destFile = new File("test4.jpg");//2.造流//2.1造节点流FileInputStream fis = new FileInputStream(srcFile);FileOutputStream fos = new FileOutputStream(destFile);//2.2造缓冲流,可以合并书写bis = new BufferedInputStream(fis);bos = new BufferedOutputStream(fos);//3.文件读取、写出操作byte[] buffer = new byte[1024];int len;while ((len = bis.read(buffer)) != -1){bos.write(buffer,0,len);}} catch (IOException e) {e.printStackTrace();} finally {//4.关闭流if (bos != null){try {bos.close();} catch (IOException e) {e.printStackTrace();}}if (bis != null){try {bis.close();} catch (IOException e) {e.printStackTrace();}}}
}
@Test
public void testBufferedReaderBufferedWriter(){BufferedReader br = null;BufferedWriter bw = null;try {//创建文件和相应的流br = new BufferedReader(new FileReader(new File("dbcp.txt")));bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt")));//读写操作//方式一:使用char[]数组// char[] cbuf = new char[1024];// int len;// while((len = br.read(cbuf)) != -1){// bw.write(cbuf,0,len);// // bw.flush();// }//方式二:使用StringString data;while((data = br.readLine()) != null){//方法一:// bw.write(data + "\n");//data中不包含换行符//方法二:bw.write(data);//data中不包含换行符bw.newLine();//提供换行的操作}} catch (IOException e) {e.printStackTrace();} finally {//关闭资源if(bw != null){try {bw.close();} catch (IOException e) {e.printStackTrace();}}if(br != null){try {br.close();} catch (IOException e) {e.printStackTrace();}}}
}
转换流
转换流提供了在字节流和字符流之间的转换
Java API提供了两个转换流:
InputstreamReader
:将Inputstream
转换为Reader
OutputStreamWriter
:将Writer
转换为OutputStream
字节流中的数据都是字符时,转成字符流操作更高效。
很多时候我们使用转换流来处理文件乱码问题。实现编码和解码的功能。
//综合使用InputStreamReader和OutputStreamWriter
@Test
public void test1() {InputStreamReader isr = null;OutputStreamWriter osw = null;try {//1.造文件、造流File file1 = new File("dbcp.txt");File file2 = new File("dbcp_gbk.txt");FileInputStream fis = new FileInputStream(file1);FileOutputStream fos = new FileOutputStream(file2);isr = new InputStreamReader(fis, "utf-8");osw = new OutputStreamWriter(fos, "gbk");//2.读写过程char[] cbuf = new char[20];int len;while ((len = isr.read(cbuf)) != -1){osw.write(cbuf,0,len);}} catch (IOException e) {e.printStackTrace();} finally {//3.关流if (isr != null){try {isr.close();} catch (IOException e) {e.printStackTrace();}}if (osw != null){try {osw.close();} catch (IOException e) {e.printStackTrace();}}}
}
对象流
ObjectOutputStream
:内存中的对象—>存储中的文件、通过网络传输出去:序列化过程ObjectInputStream
:存储中的文件、通过网络接收过来 —>内存中的对象:反序列化过程- 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原。
- 凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:
private static final long serialVersionUID;
serialVersionUID
用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序列化时是否兼容- 如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID可能发生变化。故建议显式声明。
- 简单来说,Java 的序列化机制是通过在运行时判断类的
serialversionUID
来验证版本一致性的。在进行反序列化时,JVM 会把传来的字节流中的serialversionUID
与本地相应实体类的serialversionUID
进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException
)
实现序列化
实现序列化的对象需要满足:
- 需要实现接口:
Serializable
(标识接口) - 当前类提供一个全局常量:
serialVersionUID
(序列版本号) - 除了当前类需要实现
Serializable
接口之外,还必须保证其内部所属性也必须是可序列化的。(默认情况下,基本数据类型可序列化)
补充:ObjectOutputStream
和 ObjectInputStream
不能序列化 static
和 transient
修饰的成员变量。(transient用于修饰不序列化的变量)
@Test
public void testObjectOutputStream() {ObjectOutputStream oos = null;try {//1.创建对象,创建流oos = new ObjectOutputStream(new FileOutputStream("object.dat"));//2.操作流oos.writeObject(new String("我爱北京天安门"));oos.flush();//刷新操作oos.writeObject(new Person("王铭", 23));oos.flush();oos.writeObject(new Person("张学良", 23, new Account(5000)));oos.flush();} catch (IOException e) {e.printStackTrace();} finally {if (oos != null) {//3.关闭流try {oos.close();} catch (IOException e) {e.printStackTrace();}}}
}
反序列化
@Test
public void testObjectInputStream() {ObjectInputStream ois = null;try {ois = new ObjectInputStream(new FileInputStream("object.dat"));Object obj = ois.readObject();String str = (String) obj;Person p = (Person) ois.readObject();Person p1 = (Person) ois.readObject();System.out.println(str);System.out.println(p);System.out.println(p1);} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} finally {if (ois != null) {try {ois.close();} catch (IOException e) {e.printStackTrace();}}}
}
Person
public class Person implements Serializable {private static final long serialVersionUID = 45612345L;private String name;private int age;private Account ac;public Person(String name, int age) {this.name = name;this.age = age;}public Person(String name, int age, Account ac) {this.name = name;this.age = age;this.ac = ac;}
}class Account implements Serializable{private static final long serialVersionUID = 45612312345L;private double balance;public Account(double balance) {this.balance = balance;}
}
随机存取流
RandomAccessFile
直接继承于 java.lang.Object
类,实现了 DataInput
和DataOutput
接口
RandomAccessFile
既可以作为一个输入流,又可以作为一个输出流
RandomAccessFile
类支持“随机访问”的方式,程序可以直接跳到文件的任意地方来读、写文件
- 支持只访问文件的部分内容
- 可以向已存在的文件后追加内容
RandomAccessFile
对象包含一个记录指针,用以标示当前读写处的位置
RandomaccessFile
类对象可以自由移动记录指针:
long getFilePointer()
:获取文件记录指针的当前位置void seek(long pos)
:将文件记录指针定位到pos
位置
访问模式:
- r:以只读方式打开
- rw:打开以便读取和写入
- rwd:打开以便读取和写入;同步文件内容的更新
- rws:打开以便读取和写入;同步文件内容和元数据的更新
文件读写
@Test
public void test1() {RandomAccessFile raf1 = null;RandomAccessFile raf2 = null;try {//1.创建对象,创建流raf1 = new RandomAccessFile(new File("test.jpg"),"r");raf2 = new RandomAccessFile(new File("test1.jpg"),"rw");//2.操作流byte[] buffer = new byte[1024];int len;while((len = raf1.read(buffer)) != -1){raf2.write(buffer,0,len);}} catch (IOException e) {e.printStackTrace();} finally {//3.关闭流if(raf1 != null){try {raf1.close();} catch (IOException e) {e.printStackTrace();}}if(raf2 != null){try {raf2.close();} catch (IOException e) {e.printStackTrace();}}}
}
文件插入
@Test
public void test2() {RandomAccessFile raf1 = null;try {raf1 = new RandomAccessFile(new File("hello.txt"), "rw");raf1.seek(3);//将指针调到角标为3的位置//方式一//保存指针3后面的所有数据到StringBuilder中// StringBuilder builder = new StringBuilder((int) new File("hello.txt").length());// byte[] buffer = new byte[20];// int len;// while ((len = raf1.read(buffer)) != -1) {// builder.append(new String(buffer, 0, len));// }// raf1.seek(3);// raf1.write("xyz".getBytes());// raf1.write(builder.toString().getBytes());//方式二ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] buffer = new byte[20];int len;while ((len = raf1.read(buffer)) != -1){baos.write(buffer,0,len);}// System.out.println(baos.toString());//调回指针,写入“xyz”raf1.seek(3);raf1.write("xyz".getBytes());//将StringBuilder中的数据写入到文件中raf1.write(baos.toString().getBytes());} catch (IOException e) {e.printStackTrace();} finally {if (raf1 != null) {try {raf1.close();} catch (IOException e) {e.printStackTrace();}}}
}
NIO
Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO
|-----java.nio.channels.Channel|---- FileChannel:处理本地文件|---- SocketChannel:TCP网络编程的客户端的Channel|---- ServerSocketChannel:TCP网络编程的服务器端的Channel|---- DatagramChannel:UDP网络编程中发送端和接收端的Channel
Path
Paths 类提供的静态 get() 方法用来获取 Path 对象:
static Path get(String first, String….more):用于将多个字符串串连成路径
static Path get(URI uri):返回指定 uri 对应的 Path 路径
@Test
public void test1(){Path path1 = Paths.get("hello.txt");//new File(String filepath)Path path2 = Paths.get("E:\\", "test\\test1\\haha.txt");//new File(String parent,String filename);Path path3 = Paths.get("E:\\", "test");System.out.println(path1);System.out.println(path2);System.out.println(path3);
}
String toString() : 返回调用 Path 对象的字符串表示形式
boolean startsWith(String path) : 判断是否以 path 路径开始
boolean endsWith(String path) : 判断是否以 path 路径结束
boolean isAbsolute() : 判断是否是绝对路径
Path getParent() :返回 Path 对象包含整个路径,不包含 Path 对象指定的文件路径
Path getRoot() :返回调用 Path 对象的根路径
Path getFileName() : 返回与调用 Path 对象关联的文件名
int getNameCount() : 返回 Path 根目录后面元素的数量
Path getName(int idx) : 返回指定索引位置 idx 的路径名称
Path toAbsolutePath() : 作为绝对路径返回调用 Path 对象
Path resolve(Path p) :合并两个路径,返回合并后的路径对应的 Path 对象
File toFile(): 将 Path 转化为 File 类的对象
File
用于操作文件或目录的工具类
Path copy(Path src, Path dest, CopyOption … how) : 文件的复制
要想复制成功,要求 path1 对应的物理上的文件存在。path1 对应的文件没有要求。
Files.copy(path1, path2, StandardCopyOption.REPLACE_EXISTING);Path createDirectory(Path path, FileAttribute<?> … attr) : 创建一个目录
要想执行成功,要求 path 对应的物理上的文件目录不存在。一旦存在,抛出异常。
Path createFile(Path path, FileAttribute<?> … arr) : 创建一个文件
要想执行成功,要求 path 对应的物理上的文件不存在。一旦存在,抛出异常。void delete(Path path) : 删除一个文件/目录,如果不存在,执行报错
void deleteIfExists(Path path) : Path 对应的文件/目录如果存在,执行删除.如果不存在,正常执行结束Path move(Path src, Path dest, CopyOption…how) : 将 src 移动到 dest 位置
要想执行成功,src 对应的物理上的文件需要存在,dest 对应的文件没有要求。long size(Path path) : 返回 path 指定文件的大小
boolean exists(Path path, LinkOption … opts) : 判断文件是否存在
boolean notExists(Path path, LinkOption … opts) : 判断文件是否不存在boolean isDirectory(Path path, LinkOption … opts) : 判断是否是目录
不要求此 path 对应的物理文件存在。
boolean isRegularFile(Path path, LinkOption … opts) : 判断是否是文件
boolean isHidden(Path path) : 判断是否是隐藏文件
要求此 path 对应的物理上的文件需要存在。才可判断是否隐藏。否则,抛异常。
boolean isReadable(Path path) : 判断文件是否可读
boolean isWritable(Path path) : 判断文件是否可写
InputStream newInputStream(Path path, OpenOption…how):获取 InputStream 对象
OutputStream newOutputStream(Path path, OpenOption…how) : 获取 OutputStream 对象
SeekableByteChannel newByteChannel(Path path, OpenOption…how) : 获取与指定文件的连接,how 指定打开方式。
DirectoryStream<Path> newDirectoryStream(Path path) : 打开 path 指定的目录
StandardOpenOption.READ
:表示对应的 Channel
是可读的。
StandardOpenOption.WRITE
:表示对应的 Channel
是可写的。
StandardOpenOption.CREATE
:如果要写出的文件不存在,则创建。如果存在,忽略
StandardOpenOption.CREATE_NEW
:如果要写出的文件不存在,则创建。如果存在,抛异常
@Test
public void test3() throws IOException{Path path1 = Paths.get("d:\\nio", "hello.txt");// InputStream newInputStream(Path path, OpenOption…how):获取 InputStream 对象InputStream inputStream = Files.newInputStream(path1, StandardOpenOption.READ);// OutputStream newOutputStream(Path path, OpenOption…how) : 获取 OutputStream 对象OutputStream outputStream = Files.newOutputStream(path1, StandardOpenOption.WRITE,StandardOpenOption.CREATE);// SeekableByteChannel newByteChannel(Path path, OpenOption…how) : 获取与指定文件的连接,how 指定打开方式。SeekableByteChannel channel = Files.newByteChannel(path1, StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);// DirectoryStream<Path> newDirectoryStream(Path path) : 打开 path 指定的目录Path path2 = Paths.get("e:\\teach");DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path2);Iterator<Path> iterator = directoryStream.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}
}
11.网络
端口号与 IP 地址的组合得出一个网络套接字:Socket
Socket
分类- 流套接字(
stream socket
):使用TCP提供可依赖的字节流服务 - 数据报套接字(
datagram socket
):使用UDP提供“尽力而为”的数据报服务
- 流套接字(
TCP
例1:发送信息
//客户端
@Test
public void client() {Socket socket = null;OutputStream os = null;try {//1.创建Socket对象,指明服务器端的ip和端口号InetAddress inet = InetAddress.getByName("127.0.0.1");socket = new Socket(inet, 8899);//2.获取一个输出流,用于输出数据os = socket.getOutputStream();//3.写出数据的操作os.write("你好,我是客户端".getBytes());} catch (IOException e) {e.printStackTrace();} finally {//4.资源的关闭if (os != null) {try {os.close();} catch (IOException e) {e.printStackTrace();}}if (socket != null) {try {socket.close();} catch (IOException e) {e.printStackTrace();}}}
}//服务端
@Test
public void server() {ServerSocket ss = null;Socket socket = null;InputStream is = null;ByteArrayOutputStream baos = null;try {//1.创建服务器端的ServerSocket,指明自己的端口号ss = new ServerSocket(8899);//2.调用accept()表示接收来自于客户端的socketsocket = ss.accept();//3.获取输入流is = socket.getInputStream();//不建议这样写,可能会有乱码// byte[] buffer = new byte[1024];// int len;// while((len = is.read(buffer)) != -1){// String str = new String(buffer,0,len);// System.out.print(str);// }//4.读取输入流中的数据baos = new ByteArrayOutputStream();byte[] buffer = new byte[5];int len;while ((len = is.read(buffer)) != -1) {baos.write(buffer, 0, len);}System.out.println(baos.toString());System.out.println("收到了来自于:" + socket.getInetAddress().getHostAddress() + "的数据");} catch (IOException e) {e.printStackTrace();} finally {if (baos != null) {//5.关闭资源try {baos.close();} catch (IOException e) {e.printStackTrace();}}if (is != null) {try {is.close();} catch (IOException e) {e.printStackTrace();}}if (socket != null) {try {socket.close();} catch (IOException e) {e.printStackTrace();}}if (ss != null) {try {ss.close();} catch (IOException e) {e.printStackTrace();}}}
}
例2:发送图片保存并返回信息
@Test
public void client() throws IOException {//1.Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090);//2.OutputStream os = socket.getOutputStream();//3.FileInputStream fis = new FileInputStream(new File("beauty.jpg"));//4.byte[] buffer = new byte[1024];int len;while((len = fis.read(buffer)) != -1){os.write(buffer,0,len);}//关闭数据的输出socket.shutdownOutput();//5.接收来自于服务器端的数据,并显示到控制台上InputStream is = socket.getInputStream();ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] bufferr = new byte[20];int len1;while((len1 = is.read(buffer)) != -1){baos.write(buffer,0,len1);}System.out.println(baos.toString());//6.fis.close();os.close();socket.close();baos.close();
}/*这里涉及到的异常,应该使用try-catch-finally处理*/
@Test
public void server() throws IOException {//1.ServerSocket ss = new ServerSocket(9090);//2.Socket socket = ss.accept();//3.InputStream is = socket.getInputStream();//4.FileOutputStream fos = new FileOutputStream(new File("beauty2.jpg"));//5.byte[] buffer = new byte[1024];int len;while((len = is.read(buffer)) != -1){fos.write(buffer,0,len);}System.out.println("图片传输完成");//6.服务器端给予客户端反馈OutputStream os = socket.getOutputStream();os.write("你好,美女,照片我已收到,非常漂亮!".getBytes());//7.fos.close();is.close();socket.close();ss.close();os.close();
}
UDP
以数据报方式进行传输
//发送端
@Test
public void sender() throws IOException {DatagramSocket socket = new DatagramSocket();String str = "我是UDP方式发送的导弹";byte[] data = str.getBytes();InetAddress inet = InetAddress.getLocalHost();//创建数据报DatagramPacket packet = new DatagramPacket(data, 0, data.length, inet, 9090);//发送socket.send(packet);socket.close();
}
//接收端
@Test
public void receiver() throws IOException {DatagramSocket socket = new DatagramSocket(9090);byte[] buffer = new byte[100];//创建数据报DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);//接收socket.receive(packet);//打印System.out.println(new String(packet.getData(), 0, packet.getLength()));socket.close();
}
Url
public String getProtocol() 获取该 URL 的协议名
public String getHost() 获取该 URL 的主机名
public String getPort() 获取该 URL 的端口号
public String getPath() 获取该 URL 的文件路径
public String getFile() 获取该 URL 的文件名
public String getQuery() 获取该 URL 的查询名
通过url下载
public static void main(String[] args) {HttpURLConnection urlConnection = null;InputStream is = null;FileOutputStream fos = null;try {//创建urlURL url = new URL("http://localhost:8080/examples/beauty.jpg");//创建连接urlConnection = (HttpURLConnection) url.openConnection();//开启连接urlConnection.connect();//创建传输流is = urlConnection.getInputStream();//创建接收流fos = new FileOutputStream("beauty3.jpg");byte[] buffer = new byte[1024];int len;while ((len = is.read(buffer)) != -1) {fos.write(buffer, 0, len);}System.out.println("下载完成");} catch (IOException e) {e.printStackTrace();} finally {//关闭资源if (is != null) {try {is.close();} catch (IOException e) {e.printStackTrace();}}if (fos != null) {try {fos.close();} catch (IOException e) {e.printStackTrace();}}if (urlConnection != null) {urlConnection.disconnect();}}
}
12.反射
加载完类之后,在堆内存的方法区中就产生了一个 Class
类型的对象(一个类只有一个 Class
对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
通常的方式:引入需要的“包类”名称—->通过 new
实例化—->获得实例化对象
反射的方式:实例化对象—-> getClass()
方法—->得到完整的“包类”名称
反射的动态性
只有当程序运行时我们才能知道调用的类
@Test
public void test2(){for(int i = 0;i < 100;i++){int num = new Random().nextInt(2);//0,1String classPath = "";switch(num){case 0:classPath = "java.util.Date";break;case 1:classPath = "java.lang.Object";break;}try {Object obj = getInstance(classPath);System.out.println(obj);} catch (Exception e) {e.printStackTrace();}}
}/*创建一个指定类的对象。classPath:指定类的全类名*/
public Object getInstance(String classPath) throws Exception {Class clazz = Class.forName(classPath);return clazz.newInstance();
}
反射功能
@Test
public void test1() throws Exception {Class<Person> clazz = Person.class;//1.通过反射,创建Person类对象Constructor<Person> cons = clazz.getConstructor(String.class, int.class);Person person = cons.newInstance("Tom", 12);System.out.println(person);//Person{name='Tom', age=12}//2.通过反射,调用对象指定的属性、方法//调用属性Field age = clazz.getDeclaredField("age");age.setAccessible(true);age.set(person, 10);System.out.println(person.toString());//Person{name='Tom', age=10}//调用方法Method show = clazz.getDeclaredMethod("show");show.invoke(person);//my name is Tom and age is 10System.out.println("===================================");//通过反射,可以调用Person类的私有结构的。比如:私有的构造器、方法、属性//调用私有的构造器Constructor<Person> cons1 = clazz.getDeclaredConstructor(String.class);cons1.setAccessible(true);Person p1 = cons1.newInstance("Bruce");System.out.println(p1);//Person{name='Bruce', age=0}//调用私有的属性Field name = clazz.getDeclaredField("name");name.setAccessible(true);name.set(p1, "Jarry");System.out.println(p1);//调用私有的方法Method nation = clazz.getDeclaredMethod("showNation", String.class);nation.setAccessible(true);Object nation1 = (String) nation.invoke(p1, "中国");//相当于String nation = p1.showNation("China")System.out.println(nation1);//I come from China
}
Class
Class
本身也是一个类
Class
对象只能由系统建立对象
一个加载的类在 JVM 中只会有一个 Class
实例
一个 Class
对象对应的是一个加载到 JVM 中的一个 .class
文件
每个类的实例都会记得自己是由哪个 Class
实例所生成
通过 Class
可以完整地得到一个类中的所有被加载的结构
Class
类是 Reflection 的根源,针对任何你想动态加载、运行的类,唯有先获得相应的 Class
对象
类加载过程
程序经过 javac.exe
命令以后,会生成一个或多个字节码文件(.class
结尾)。接着我们使用 java.exe
命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为 Class
的一个实例。
换句话说,Class
的实例就对应着一个运行时类。
加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
常用方法
获取Class实例方式
1)已知具体的类,通过类的 class
属性获取,该方法最为安全可靠,程序性能最高 实例:Class clazz = String.class;
2)已知某个类的实例,调用该实例的 getclass()
方法获取 Class
对象 实例:Class clazz=person.getclass();
3)已知一个类的全类名,且该类在类路径下,可通过 Class
类的静态方法 forName()
获取,可能抛出 ClassNotFoundException
(比较常用)
4)通过类加载器 ClassLoader cl = this.getclass().getClassLoader();
Class clazz = cl.loadClass("类的全类名");
@Test
public void test2() throws ClassNotFoundException {//方式一:调用运行时类的属性:.classClass<Person> clazz1 = Person.class;System.out.println(clazz1);//方式二:通过运行时类的对象,调用getClass()Person p1 = new Person();Class<? extends Person> clazz2 = p1.getClass();System.out.println(clazz2);//方式三:调用Class的静态方法:forName(String classPath)Class<?> clazz3 = Class.forName("Person");//类的全类名System.out.println(clazz3);System.out.println(clazz1 == clazz2);//trueSystem.out.println(clazz1 == clazz3);//true//方式四:使用类的加载器:ClassLoader (了解)ClassLoader classLoader = C_GetClass.class.getClassLoader();Class<?> clazz4 = classLoader.loadClass("Person");System.out.println(clazz4);//class cn.bruce.java.PersonSystem.out.println(clazz1 == clazz4);//true
}
Class代表结构
@Test
public void test3(){Class c1 = Object.class;Class c2 = Comparable.class;Class c3 = String[].class;Class c4 = int[][].class;Class c5 = ElementType.class;Class c6 = Override.class;Class c7 = int.class;Class c8 = void.class;Class c9 = Class.class;int[] i1 = new int[10];int[] i2 = new int[100];Class c10 = i1.getClass();Class c11 = i2.getClass();// 只要数组的元素类型与维度一样,就是同一个ClassSystem.out.println(c10 == c11);//true
}
类加载器
将 class
文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的 java.lang.Class
对象,作为方法区中类数据的访问入口。
类缓存:标准的 JavaSE
类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些 Class
对象
分类
@Test
public void test1(){//对于自定义类,使用系统类加载器进行加载ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();System.out.println(classLoader);//调用系统类加载器的getParent():获取扩展类加载器ClassLoader classLoader1 = classLoader.getParent();System.out.println(classLoader1);//调用扩展类加载器的getParent():无法获取引导类加载器//引导类加载器主要负责加载java的核心类库,无法加载自定义类的。ClassLoader classLoader2 = classLoader1.getParent();System.out.println(classLoader2);ClassLoader classLoader3 = String.class.getClassLoader();System.out.println(classLoader3);
}
使用Classloader加载src目录下的配置文件
@Test
public void test3(){Properties pros = new Properties();//读取配置文件的方式一://此时的文件默认在当前的module下。// FileInputStream fis = null;// try {// fis = new FileInputStream("jdbc.properties");// pros.load(fis);// } catch (IOException e) {// e.printStackTrace();// } finally {// if (fis != null) {// try {// fis.close();// } catch (IOException e) {// e.printStackTrace();// }// }// }//读取配置文件的方式二:使用ClassLoader//配置文件默认识别为:当前module的src下ClassLoader classLoader = E_Properties.class.getClassLoader();InputStream is = classLoader.getResourceAsStream("jdbc1.properties");try {pros.load(is);} catch (IOException e) {e.printStackTrace();}String user = pros.getProperty("user");String password = pros.getProperty("password");System.out.println("user = " + user + " password = " + password);
}
创建运行时类的对象
newInstance()
:调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。
要想此方法正常的创建运行时类的对象,要求:
- 运行时类必须提供空参的构造器
- 空参的构造器的访问权限得够。通常,设置为
public
。
在 javabean
中要求提供一个 public
的空参构造器。原因:
- 便于通过反射,创建运行时类的对象
- 便于子类继承此运行时类时,默认调用
super()
时,保证父类此构造器
//newInstance()
@Test
public void test1() throws Exception {//方式一Class<Person> clazz1 = Person.class;//方式二Class<Person> clazz2 = (Class<Person>) Class.forName("cn.bruce.java.Person");Person person1 = clazz1.newInstance();Person person2 = clazz2.newInstance();System.out.println(person1);System.out.println(person2);}
获取运行时类的结构
我们可以通过反射,获取对应的运行时类中所有的属性、方法、构造器、父类、接口、父类的泛型、包、注解、异常等。。。。
实现的全部接口: public Class<?>[] getInterfaces() 确定此对象所表示的类或接口实现的接口。所继承的父类: public Class<? Super T> getSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。全部的构造器:
public Constructor<T>[] getConstructors()
返回此 Class 对象所表示的类的所有 public 构造方法public Constructor<T>[] getDeclaredConstructors()
返回此Class对象表示的类声明的所有构造方法。
在Constructor类中:
取得修饰符:public int getModifiers();
取得方法名称: public String getName();
取得参数的类型: public Class<?> getParameterTypes();全部的方法:
public Method[] getDeclaredMethods()
返回此Class对象所表示的类或接口的全部方法
public Method[] getMethods()
返回此 Class 对象所表示的类或接口的 public 的方法
Method 类中:public Class<?> getReturnType():取得全部的返回值
public Class<?>[] getParameterTypes():取得全部的参数
public int getModifiers():取得修饰符
public Class<?> [] getEXceptionTypes():取得异常信息全部的 Field:
public Field[] getFields()
返回此 Class 对象所表示的类或接口的 public 的 Field。
public Field[] getDeclaredFields()
返回此 Class 对象所表示的类或接口的全部 Field
Field 方法中public int getModifiers():以整数形式返回此 Field 的修饰符
public Class<?> getType():得到 Field 的属性类型
public String getName():返回 Field 的名称。Annotation 相关
get Annotation(Class<T> annotationClass)
getDeclaredAnnotations()泛型相关
获取父类泛型类型:Type getGenericSuperclass()
泛型类型:ParameterizedType
获取实际的泛型类型参数数组:getActualTypeArguments()类所在的包 Package getPackage()
详细实现见代码
调用运行时类的指定结构
调用指定属性
@Test
public void testField() throws Exception {Class clazz = Person.class;//创建运行时类的对象Person p = (Person) clazz.newInstance();//1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性Field name = clazz.getDeclaredField("name");//2.保证当前属性是可访问的name.setAccessible(true);//3.获取、设置指定对象的此属性值name.set(p,"Tom");System.out.println(name.get(p));
}
调用指定方法
Object invoke(object obj,Object… args)方法:
Object
对应原方法的返回值,若原方法无返回值,此时返回 null
若原方法若为静态方法,此时形参 Object obj
可为 null
若原方法形参列表为空,则 Object[] args
为 null
若原方法声明为 private
,则需要在调用此 invoke()
方法前,显式调用方法对象的 setAccessible(true)
方法,将可访问 private
的方法。
Method
和Field
、Constructor
对象都有setAccessible()
方法。setAccessible
是启动和禁用访问安全检查的开关
@Test
public void testMethod() throws Exception {Class<Person> clazz = Person.class;//创建运行时类的对象Person person = clazz.newInstance();/*1.获取指定的某个方法getDeclaredMethod():参数1 :指明获取的方法的名称 参数2:指明获取的方法的形参列表*/Method showNation = clazz.getDeclaredMethod("showNation", String.class);//2.保证当前方法是可访问的showNation.setAccessible(true);/*3. 调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参invoke()的返回值即为对应类中调用的方法的返回值。*/Object returnValue = showNation.invoke(person, "CHN");System.out.println(returnValue);System.out.println("*************如何调用静态方法*****************");Method showDesc = clazz.getDeclaredMethod("showDesc");showDesc.setAccessible(true);//如果调用的运行时类中的方法没有返回值,则此invoke()返回null//Object returnVal = showDesc.invoke(null);Object returnVal = showDesc.invoke(Person.class);System.out.println(returnVal);
}
调用指定构造器
@Test
public void testConstructor() throws Exception {Class clazz = Person.class;//private Person(String name)/*1.获取指定的构造器getDeclaredConstructor():参数:指明构造器的参数列表*/Constructor constructor = clazz.getDeclaredConstructor(String.class);//2.保证此构造器是可访问的constructor.setAccessible(true);//3.调用此构造器创建运行时类的对象Person per = (Person) constructor.newInstance("Tom");System.out.println(per);
}
动态代理
静态地理
静态代理缺点
① 代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。
② 每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。
interface ClothFactory{void produceCloth();
}//被代理类
class NikeClothFactory implements ClothFactory{@Overridepublic void produceCloth() {System.out.println("Nike 生产衣服");}
}
//代理类
class ProxyClothFactory implements ClothFactory{private ClothFactory factory;//用被代理类对象进行实例化public ProxyClothFactory(ClothFactory factory) {this.factory = factory;}@Overridepublic void produceCloth() {System.out.println("代理工厂做一些准备工作");factory.produceCloth();System.out.println("代理工厂做一些后续的收尾工作");}
}
//测试
public class StaticProxyTest {public static void main(String[] args) {//创建被代理类的对象ClothFactory nike = new NikeClothFactory();//创建代理类的对象ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike);proxyClothFactory.produceCloth();}
}
动态代理
动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
(通过 Proxy.newProxyInstance()
实现)
问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法。
(通过 InvocationHandler
接口的实现类及其方法 invoke()
)
动态代理实现步骤:
- 创建一个实现接口
InvocationHandler
的类,它必须实现invoke方法,以完成代理的具体操作。 - 创建被代理类以及接口
- 通过Proxy的静态方法
newProxyInstance(ClassLoader loader, Class<?>...interface, InvocationHandler h)
创建一个接口代理 - 通过代理类的实例调用被代理类的方法
interface Human {String getBelief();void eat(String food);
}//被代理类
class SuperMan implements Human {@Overridepublic String getBelief() {return "I believe I can fly!";}@Overridepublic void eat(String food) {System.out.println("I like eat " + food);}
}
/*
要想实现动态代理,需要解决的问题?
问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。*/
class ProxyFactory {//调用此方法,返回一个代理类的对象。解决问题一public static Object getProxyInstance(Object obj) {MyInvocationHanlder hanlder = new MyInvocationHanlder();hanlder.bind(obj);//newProxyInstance(ClassLoader loader,@NotNull Class<?>[] interfaces,@NotNull reflect.InvocationHandler h)return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), hanlder);}
}//创建继承了InvocationHandler接口的类,解决问题二
class MyInvocationHanlder implements InvocationHandler {private Object obj;//需要使用被代理类的对象进行赋值public void bind(Object obj) {this.obj = obj;}//当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()//将被代理类要执行的方法a的功能就声明在invoke()中@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法//obj:被代理类的对象Object returnValue = method.invoke(obj, args);//上述方法的返回值就作为当前类中的invoke()的返回值。return returnValue;}
}//测试动态代理
public class H_ADP {public static void main(String[] args) {SuperMan superMan = new SuperMan();//proxyInstance:代理类的对象Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);//当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法String belief = proxyInstance.getBelief();System.out.println(belief);proxyInstance.eat("火锅");}
}
Junit
导入junit包,创建新文件,带上@Test注解