九、面向对象

三、多态

多态理解

1.多态指的是:
父类型引|用指向子类型对象。
●编译阶段:绑定父类的方法。
●运行阶段: 动态绑定子类型对象的方法。
2.运算符:instanceof(运行阶段动态判断)

  • instanceof运算符的运算结果只能是:true/false
  • 假设(c instanceof Cat)为true表示:

c引用指向的堆内存中的java对像是一个Cat。

  • 假设(c instanceof Cat)为false表示:

c引用指向的堆内存中的java对象不是一个Cat

多态在实际开发中的作用

多态使用之前

  1. public class Dog {
  2. public void eat(){
  3. System.out.println("狗狗喜欢吃骨头,吃的很香");
  4. }
  5. }
  1. public class Cat {
  2. public void eat(){
  3. System.out.println("猫喜欢吃鱼,吃的很香");
  4. }
  5. }
  1. public class Master {
  2. public void feed(Dog d){
  3. d.eat();
  4. }
  5. public void feed(Cat c){
  6. c.eat();
  7. }
  1. public class Test {
  2. public static void main(String[] args) {
  3. Master master = new Master();
  4. Dog dog = new Dog();
  5. master.feed(dog);
  6. Cat cat = new Cat();
  7. master.feed(cat);
  8. }
  9. }

多态使用之后

  1. public class Pet {
  2. public void eat(){
  3. }
  4. }
  1. public class Dog extends Pet{
  2. public void eat(){
  3. System.out.println("狗狗喜欢吃骨头,吃的很香");
  4. }
  5. }
  1. public class Cat extends Pet{
  2. public void eat(){
  3. System.out.println("猫喜欢吃鱼,吃的很香");
  4. }
  5. }
  1. public class Master {
  2. public void feed(Pet pet){
  3. //编译的时候,编译器发现pet是Pet类,会去Pet类中找eat()方法,结果找到了,编译通过。
  4. //运行的时候,底层实际的对象是什么,就自动调用到该实际对象对应的eat()方法上。
  5. //这就是多态的使用。
  6. pet.eat();
  7. }
  8. }
  1. public class Test {
  2. public static void main(String[] args) {
  3. Master master = new Master();
  4. Dog dog = new Dog();
  5. master.feed(dog);
  6. Cat cat = new Cat();
  7. master.feed(cat);
  8. }
  9. }

四、接口和抽象类

接口在开发中的作用

  1. public interface FoodMeau{
  2. void xihongshi();
  3. void qiezi();
  4. }
  1. public class ChinaCook implements FoodMeau{
  2. public void xihongshi(){
  3. System.out.println("中国西红柿");
  4. }
  5. public void qiezi(){
  6. System.out.println("中国茄子");
  7. }
  8. }
  1. public class AmericCook implements FoodMeau {
  2. public void xihongshi(){
  3. System.out.println("美国西红柿");
  4. }
  5. public void qiezi(){
  6. System.out.println("美国茄子");
  7. }
  8. }
  1. public class Customer {
  2. private FoodMeau foodMeau;
  3. public Customer(){
  4. }
  5. public Customer(FoodMeau foodMeau){
  6. this.foodMeau=foodMeau;
  7. }
  8. public void setFoodMeau( FoodMeau foodMeau){
  9. this.foodMeau=foodMeau;
  10. }
  11. public FoodMeau getFoodMeau(){
  12. return foodMeau;
  13. }
  14. public void order(){
  15. foodMeau.xihongshi();
  16. foodMeau.qiezi();
  17. }
  18. }
  1. public class Test {
  2. public static void main(String[] args) {
  3. //创建厨师对象
  4. FoodMeau cook1 = new ChinaCook();
  5. //创建顾客对象
  6. Customer customer = new Customer(cook1);;//如果去掉参数,则空指针异常
  7. customer.order();//中国西红柿
  8. //中国茄子
  9. }
  10. }

十、反射

1、获得Class对象

第一种: Class c = Class.forName(“完整的类名带包名”);
第二种: Class c = 对象.getClass();
第三种: Class c = 任何类型.class;
注意:Class.forName是一个静态方法。

如果你只是希望一个类的静态代码块执行,其它代码一律不执行,你可以使用:Class.forName(“完整类名”); 这个方法的执行会导致类加载,类加载时,静态代码块执行。

2、判断是否为某个类的实例

一般地,我们用 instanceof 关键字来判断是否为某个类的实例。同时我们也可以借助反射中 Class 对象的 isInstance() 方法来判断是否为某个类的实例,它是一个 native 方法:
public native boolean isInstance(Object obj);

3、创建实例

通过反射来生成对象主要有两种方式。

  • 使用Class对象的newInstance()方法来创建Class对象对应类的实例。

    1. Class<?> c = String.class;
    2. Object str = c.newInstance();

    注意: 重点是:new Instance()调用的是无参构造,必须保证无参构造是存在的!

  • 先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。

    1. //获取String所对应的Class对象
    2. Class<?> c = String.class;
    3. //获取String类带一个String参数的构造器
    4. Constructor constructor = c.getConstructor(String.class);
    5. //根据构造器创建实例
    6. Object obj = constructor.newInstance("23333");
    7. System.out.println(obj);

    4.获取方法

    获取某个Class对象的方法集合,主要有以下几个方法:

  • getDeclaredMethods 方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

    1. public Method[] getDeclaredMethods() throws SecurityException
  • getMethods 方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。

    1. public Method[] getMethods() throws SecurityException
  • getMethod 方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象。

    1. public Method getMethod(String name, Class<?>... parameterTypes)

    只是这样描述的话可能难以理解,我们用例子来理解这三个方法:

    1. package org.ScZyhSoft.common;
    2. import java.lang.reflect.InvocationTargetException;
    3. import java.lang.reflect.Method;
    4. public class test1 {
    5. public static void test() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    6. Class<?> c = methodClass.class;
    7. Object object = c.newInstance();
    8. Method[] methods = c.getMethods();
    9. Method[] declaredMethods = c.getDeclaredMethods();
    10. //获取methodClass类的add方法
    11. Method method = c.getMethod("add", int.class, int.class);
    12. //getMethods()方法获取的所有方法
    13. System.out.println("getMethods获取的方法:");
    14. for(Method m:methods)
    15. System.out.println(m);
    16. //getDeclaredMethods()方法获取的所有方法
    17. System.out.println("getDeclaredMethods获取的方法:");
    18. for(Method m:declaredMethods)
    19. System.out.println(m);
    20. }
    21. }
    22. class methodClass {
    23. public final int fuck = 3;
    24. public int add(int a,int b) {
    25. return a+b;
    26. }
    27. public int sub(int a,int b) {
    28. return a+b;
    29. }
    30. }

    程序运行结果如下:

    1. Methods获取的方法:
    2. public int org.ScZyhSoft.common.methodClass.add(int,int)
    3. public int org.ScZyhSoft.common.methodClass.sub(int,int)
    4. public final void java.lang.Object.wait() throws java.lang.InterruptedException
    5. public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
    6. public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
    7. public boolean java.lang.Object.equals(java.lang.Object)
    8. public java.lang.String java.lang.Object.toString()
    9. public native int java.lang.Object.hashCode()
    10. public final native java.lang.Class java.lang.Object.getClass()
    11. public final native void java.lang.Object.notify()
    12. public final native void java.lang.Object.notifyAll()
    13. getDeclaredMethods获取的方法:
    14. public int org.ScZyhSoft.common.methodClass.add(int,int)
    15. public int org.ScZyhSoft.common.methodClass.sub(int,int)

    可以看到,通过 getMethods() 获取的方法可以获取到父类的方法,比如 java.lang.Object 下定义的各个方法。

    5、获取类的成员变量(字段)信息

    获取成员变量并调用:
    1.批量的
    1).Field[] getFields():获取所有的”公有字段”
    2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;但不包括父类的成员变量。
    2.获取单个的:
    1).public Field getField(String fieldName):获取某个”公有的”字段;
    2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)

5.2、Field:成员变量

(1)设置值void set(Object obj , Object value)
(2)获取值 get(Object obj)
(3)忽略访问权限修饰符的安全检查 setAccessible(true):暴力反射

5、3通过反射访问对象属性

Student类:

  1. package com.bjpowernode;
  2. //反射属性Field
  3. public class Student {
  4. //Field翻译为字段,其实就是属性/成员
  5. private String name;//Field对象
  6. protected int age;
  7. boolean sex;
  8. public int no;
  9. public static final double MATH_PI=3.1415926;
  10. }

测试类:

  1. package com.bjpowernode;
  2. import java.lang.reflect.Field;
  3. public class FieldTest {
  4. public static void main(String[] args) throws Exception{
  5. //使用反射机制,去访问一个对象属性(set,get)
  6. Class studentCLass = Class.forName("com.bjpowernode.Student");
  7. Object obj=studentCLass.newInstance();//obj就是Student对象(底层调用无参构造方法)
  8. //获取no属性(根据属性的名称来获取Field)
  9. Field noField = studentCLass.getDeclaredField("no");
  10. noField.set(obj,2222);
  11. System.out.println(noField.get(obj));//2222
  12. //可以访问私有属性(打破封装)
  13. Field nameField = studentCLass.getDeclaredField("name");
  14. nameField.setAccessible(true);
  15. //给name属性赋值
  16. nameField.set(obj,"jackson");
  17. //获取name属性的值
  18. System.out.println(nameField.get(obj));//jackson
  19. }
  20. }

6、通过反射调用方法

当我们从类中获取了一个方法后,我们就可以用 invoke() 方法来调用这个方法。invoke 方法的原型为:

  1. public Object invoke(Object obj, Object... args)
  2. throws IllegalAccessException, IllegalArgumentException,
  3. InvocationTargetException

下面是一个实例:

  1. public class test1 {
  2. public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
  3. Class<?> klass = methodClass.class;
  4. //创建methodClass的实例
  5. Object obj = klass.newInstance();
  6. //获取methodClass类的add方法
  7. Method method = klass.getMethod("add",int.class,int.class);
  8. //调用method对应的方法 => add(1,4)
  9. Object result = method.invoke(obj,1,4);
  10. System.out.println(result);
  11. }
  12. }
  13. class methodClass {
  14. public final int fuck = 3;
  15. public int add(int a,int b) {
  16. return a+b;
  17. }
  18. public int sub(int a,int b) {
  19. return a+b;
  20. }
  21. }

7、通过反射运行配置文件内容

Student类:

  1. package com.bjpowernode.Test;
  2. public class Student {
  3. public void show(){
  4. System.out.println("is show()");
  5. }
  6. }

配置文件以pro.txt文件为例子:

  1. className = com.bjpowernode.Test.Student
  2. methodName = show

测试类:

  1. package com.bjpowernode.Test;
  2. import java.io.FileReader;
  3. import java.lang.reflect.Constructor;
  4. import java.lang.reflect.Method;
  5. import java.util.Properties;
  6. public class Test {
  7. public static void main(String[] args) throws Exception {
  8. //加载键值对数据
  9. Properties properties = new Properties();
  10. FileReader fileReader = new FileReader("pro.txt");
  11. properties.load(fileReader);
  12. fileReader.close();
  13. //获取数据
  14. String classname= properties.getProperty("className"); //com.bjpowernode.Test.Student
  15. String methodName= properties.getProperty("methodName"); //show
  16. //反射
  17. Class c=Class.forName(classname);
  18. Constructor con = c.getConstructor();
  19. Object obj=con.newInstance();
  20. //调用方法
  21. Method m =c.getMethod(methodName);
  22. m.invoke(obj);
  23. }
  24. }

输出: is show()

8.资源绑定器

Classinfo2.properties文件:

className=123;

  1. /*
  2. java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。
  3. 使用以下这种方式的时候,属性配置文件xxx.properties必须放到类路径下。
  4. */
  5. public class ResourceBundleTest{
  6. public static void main(String[] args){
  7. 资源绑定器,只能绑定xxx.properties文件。并且这个文件必须在类路径下
  8. 文件扩展名也必须是properties
  9. 并且在写路径的时候,路径后面的扩展名不能写。
  10. ResourceBundle bundle = ResourceBundle.getBundle("classinfo2");
  11. String className = bundle.getString("className");
  12. System.out.println(className); //123
  13. }
  14. }

9、利用反射创建数组

数组在Java里是比较特殊的一种类型,它可以赋值给一个Object Reference。下面我们看一看利用反射创建数组的例子:

  1. public static void testArray() throws ClassNotFoundException {
  2. Class<?> cls = Class.forName("java.lang.String");
  3. Object array = Array.newInstance(cls,25);
  4. //往数组里添加内容
  5. Array.set(array,0,"hello");
  6. Array.set(array,1,"Java");
  7. Array.set(array,2,"fuck");
  8. Array.set(array,3,"Scala");
  9. Array.set(array,4,"Clojure");
  10. //获取某一项的内容
  11. System.out.println(Array.get(array,3));
  12. }

其中的Array类为java.lang.reflect.Array类。我们通过Array.newInstance()创建数组对象,它的原型是:

  1. public static Object newInstance(Class<?> componentType, int length)
  2. throws NegativeArraySizeException {
  3. return newArray(componentType, length);
  4. }

而 newArray 方法是一个 native 方法,它在 HotSpot JVM 里的具体实现我们后边再研究,这里先把源码贴出来:

  1. private static native Object newArray(Class<?> componentType, int length)
  2. throws NegativeArraySizeException

源码目录:openjdk\hotspot\src\share\vm\runtime\reflection.cpp

  1. arrayOop Reflection::reflect_new_array(oop element_mirror, jint length, TRAPS) {
  2. if (element_mirror == NULL) {
  3. THROW_0(vmSymbols::java_lang_NullPointerException());
  4. }
  5. if (length < 0) {
  6. THROW_0(vmSymbols::java_lang_NegativeArraySizeException());
  7. }
  8. if (java_lang_Class::is_primitive(element_mirror)) {
  9. Klass* tak = basic_type_mirror_to_arrayklass(element_mirror, CHECK_NULL);
  10. return TypeArrayKlass::cast(tak)->allocate(length, THREAD);
  11. } else {
  12. Klass* k = java_lang_Class::as_Klass(element_mirror);
  13. if (k->oop_is_array() && ArrayKlass::cast(k)->dimension() >= MAX_DIM) {
  14. THROW_0(vmSymbols::java_lang_IllegalArgumentException());
  15. }
  16. return oopFactory::new_objArray(k, length, THREAD);
  17. }
  18. }

10、通过反射越过泛型检查

泛型用在编译期,编译过后泛型擦除(消失掉),所以是可以通过反射越过泛型检查的
测试类:

  1. import java.lang.reflect.Method;
  2. import java.util.ArrayList;
  3. /*
  4. * 通过反射越过泛型检查
  5. * 例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值?
  6. */
  7. public class Demo {
  8. public static void main(String[] args) throws Exception{
  9. ArrayList<String> strList = new ArrayList<>();
  10. strList.add("aaa");
  11. strList.add("bbb");
  12. // strList.add(100);
  13. //获取ArrayList的Class对象,反向的调用add()方法,添加数据
  14. Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
  15. //获取add()方法
  16. Method m = listClass.getMethod("add", Object.class);
  17. //调用add()方法
  18. m.invoke(strList, 100);
  19. //遍历集合
  20. for(Object obj : strList){
  21. System.out.println(obj);
  22. }
  23. }
  24. }

控制台输出:

  1. aaa
  2. bbb
  3. 100

十一、IO流

一、四大家族的首领:

java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流

java.io.Reader 字符输入流
java.io.Writer 字符输出流

四大家族的首领都是抽象类。(abstract class)


java.io包下需要掌握的流有16个:

文件专属:
java.io.FileInputStream(掌握)
java.io.FileOutputStream(掌握)
java.io.FileReader
java.io.FileWriter

转换流:(将字节流转换成字符流)
java.io.InputStreamReader
java.io.OutputStreamWriter

缓冲流专属:
java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedInputStream
java.io.BufferedOutputStream

数据流专属:
java.io.DataInputStream
java.io.DataOutputStream

标准输出流:
java.io.PrintWriter
java.io.PrintStream(掌握)

对象专属流:
java.io.ObjectInputStream(掌握)
java.io.ObjectOutputStream(掌握)

二、举例

1.FileInputStream

方法摘要 方法 作用
abstract int read() 从输入流中读取数据的下一个字节
int read(byte[] b) 将输入流中读取一定数量 并将其存储在缓冲区数组 b 中。
int read(byte[] b, int off, int len) 将输入流中最多 len 个数据字节读入 byte 数组。返回总字节数.

演示read[byte[] b]:
image.png

  1. import java.io.FileInputStream;
  2. import java.io.FileNotFoundException;
  3. import java.io.IOException;
  4. public class Finally {
  5. public static void main(String[] args) {
  6. FileInputStream fis =null;
  7. try {
  8. fis=new FileInputStream("E:\\IO\\ceshi\\finally.text");
  9. //准备一个byte数组
  10. byte[] bytes=new byte[4];
  11. int readCount=0;
  12. //read(bytes)方法是读到的字节个数为几个,当读到没有时返回-1
  13. while ((readCount=fis.read(bytes))!=-1){
  14. //把byte数组转化为字符串,读到几个转换几个
  15. System.out.print(new String(bytes,0,readCount));
  16. }
  17. } catch (FileNotFoundException e) {
  18. e.printStackTrace();
  19. } catch (IOException e) {
  20. e.printStackTrace();
  21. }finally {
  22. if(fis!=null){
  23. try {
  24. fis.close();
  25. } catch (IOException e) {
  26. e.printStackTrace();
  27. }
  28. }
  29. }
  30. }
  31. }

abcdef

2.FileOutputStream

  1. import java.io.FileNotFoundException;
  2. import java.io.FileOutputStream;
  3. import java.io.IOException;
  4. public class OutTest {
  5. public static void main(String[] args) {
  6. FileOutputStream fos=null;
  7. try {
  8. //这种方式谨慎使用,这种方式会先将原文件清空,然后重新写入
  9. // fos=new FileOutputStream("E:\\IO\\ceshi\\finally.text");
  10. //以追加的方式在文件末尾写入。不会清除原文件内容。
  11. fos=new FileOutputStream("E:\\IO\\ceshi\\finally.text",true);
  12. byte[] bytes ={97,98,99,100};
  13. //将byte数组全部写出!
  14. fos.write(bytes);//abcd
  15. //将byte数组一部分写出!
  16. fos.write(bytes,0,2);//再写出ab
  17. //写完之后一定要刷新
  18. fos.flush();
  19. } catch (FileNotFoundException e) {
  20. e.printStackTrace();
  21. } catch (IOException e) {
  22. e.printStackTrace();
  23. }finally {
  24. if(fos!=null){
  25. try {
  26. fos.close();
  27. } catch (IOException e) {
  28. e.printStackTrace();
  29. }
  30. }
  31. }
  32. }
  33. }

3.所有文件的复制

  1. import java.io.FileInputStream;
  2. import java.io.FileNotFoundException;
  3. import java.io.FileOutputStream;
  4. import java.io.IOException;
  5. public class OutTest {
  6. public static void main(String[] args) {
  7. FileInputStream fis=null;
  8. FileOutputStream fos=null;
  9. try {
  10. fis=new FileInputStream("E:\\IO\\ceshi\\resource.txt");
  11. fos=new FileOutputStream("E:\\IO\\ceshi2\\position.txt");
  12. //最核心的:一边读,一边写
  13. byte [] bytes = new byte[1024*1024];//1MB(最多可拷贝1MB)
  14. int readCount=0;
  15. while ((readCount=fis.read(bytes))!=-1){
  16. fos.write(bytes,0,readCount);
  17. }
  18. } catch (FileNotFoundException e) {
  19. e.printStackTrace();
  20. } catch (IOException e) {
  21. e.printStackTrace();
  22. }finally {
  23. //分开try,不要一起try
  24. if(fis!=null){
  25. try {
  26. fis.close();
  27. } catch (IOException e) {
  28. e.printStackTrace();
  29. }
  30. }
  31. if(fos!=null){
  32. try {
  33. fos.close();
  34. } catch (IOException e) {
  35. e.printStackTrace();
  36. }
  37. }
  38. }
  39. }
  40. }

3.1所有目录的赋复制

  1. package com.bjpowernode.java.io;
  2. import java.io.*;
  3. /*
  4. 拷贝目录
  5. */
  6. public class CopyAll {
  7. public static void main(String[] args) {
  8. // 拷贝源
  9. File srcFile = new File("D:\\course\\02-JavaSE\\document");
  10. // 拷贝目标
  11. File destFile = new File("C:\\a\\b\\c");
  12. // 调用方法拷贝
  13. copyDir(srcFile, destFile);
  14. }
  15. /**
  16. * 拷贝目录
  17. * @param srcFile 拷贝源
  18. * @param destFile 拷贝目标
  19. */
  20. private static void copyDir(File srcFile, File destFile) {
  21. if(srcFile.isFile()) {
  22. // srcFile如果是一个文件的话,递归结束。
  23. // 是文件的时候需要拷贝。
  24. // ....一边读一边写。
  25. FileInputStream in = null;
  26. FileOutputStream out = null;
  27. try {
  28. // 读这个文件
  29. // D:\course\02-JavaSE\document\JavaSE进阶讲义\JavaSE进阶-01-面向对象.pdf
  30. in = new FileInputStream(srcFile);
  31. // 写到这个文件中
  32. // C:\course\02-JavaSE\document\JavaSE进阶讲义\JavaSE进阶-01-面向对象.pdf
  33. String path = (destFile.getAbsolutePath().endsWith("\\") ? destFile.getAbsolutePath() : destFile.getAbsolutePath() + "\\") + srcFile.getAbsolutePath().substring(3);
  34. out = new FileOutputStream(path);
  35. // 一边读一边写
  36. byte[] bytes = new byte[1024 * 1024]; // 一次复制1MB
  37. int readCount = 0;
  38. while((readCount = in.read(bytes)) != -1){
  39. out.write(bytes, 0, readCount);
  40. }
  41. out.flush();
  42. } catch (FileNotFoundException e) {
  43. e.printStackTrace();
  44. } catch (IOException e) {
  45. e.printStackTrace();
  46. } finally {
  47. if (out != null) {
  48. try {
  49. out.close();
  50. } catch (IOException e) {
  51. e.printStackTrace();
  52. }
  53. }
  54. if (in != null) {
  55. try {
  56. in.close();
  57. } catch (IOException e) {
  58. e.printStackTrace();
  59. }
  60. }
  61. }
  62. return;
  63. }
  64. // 获取源下面的子目录
  65. File[] files = srcFile.listFiles();
  66. for(File file : files){
  67. // 获取所有文件的(包括目录和文件)绝对路径
  68. //System.out.println(file.getAbsolutePath());
  69. if(file.isDirectory()){
  70. // 新建对应的目录
  71. //System.out.println(file.getAbsolutePath());
  72. //D:\course\02-JavaSE\document\JavaSE进阶讲义 源目录
  73. //C:\course\02-JavaSE\document\JavaSE进阶讲义 目标目录
  74. String srcDir = file.getAbsolutePath();
  75. String destDir = (destFile.getAbsolutePath().endsWith("\\") ? destFile.getAbsolutePath() : destFile.getAbsolutePath() + "\\") + srcDir.substring(3);
  76. File newFile = new File(destDir);
  77. if(!newFile.exists()){
  78. newFile.mkdirs();
  79. }
  80. }
  81. // 递归调用
  82. copyDir(file, destFile);
  83. }
  84. }
  85. }

4.java.io.BufferedReader和转换流

  1. import java.io.*;
  2. /*
  3. 带有缓冲区的字符输入流
  4. 使用这个流的时候不需要自定义char数组,或者说不需要自定义byte数组,自带缓冲。
  5. */
  6. public class BufferedReaderTest {
  7. public static void main(String[] args) throws IOException {
  8. /*
  9. 这个构造方法只能传一个字符流。不能传字节流//通过转换流转换(InputStreamReader将字节流转换为字符流)
  10. FileInputStream是节点流,BufferedReader是包装流。
  11. */
  12. BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("E:\\IO\\ceshi\\finally.text")));
  13. //br.readLine读一行
  14. String Line = null;
  15. while ((Line = br.readLine()) != null) {
  16. System.out.println(Line);
  17. }
  18. //关闭流
  19. //对于包装流来说,只需要关闭最外层流就行,里面的节点流自动关闭(可以看源码)
  20. br.close();
  21. }
  22. }

5.DataOutputStream和DataInputStream

  1. /*java.io.DataOutputStream:数据专属的流。
  2. 这个流可以将数据连同数据的类型一并写入文件。
  3. 注意:这个文件不是普通文本文档。(这个文件使用记事本打不开。)
  4. */
  5. /*
  6. DataInputStream:数据字节输入流。
  7. DataOutputStream写的文件,只能使用DataInputStream去读。
  8. 并且读的时候你需要提前知道写入的顺序。读的顺序需要和写的顺序一致。才可以正常取出数据。
  9. */
  10. import java.io.DataOutputStream;
  11. import java.io.FileOutputStream;
  12. import java.io.IOException;
  13. public class Data {
  14. public static void main(String[] args) throws IOException {
  15. //创建数据专属的字节输出流
  16. //Dataoutputstream(Outputstream) Outputstream是抽象类无法实例化,我们可以new 他的子类
  17. DataOutputStream dos=new DataOutputStream(new FileOutputStream("E:\\IO\\ceshi\\finally.text"));
  18. //写数据
  19. int i=300;
  20. boolean sex=false;
  21. char c='a';
  22. //写
  23. dos.writeInt(i);
  24. dos.writeBoolean(sex);
  25. dos.writeChar(c);
  26. //刷新
  27. dos.flush();
  28. //关闭最外层
  29. dos.close();
  30. }
  31. }

6.标准输出流

标准输出流是不需要关闭流的

  1. import java.io.FileNotFoundException;
  2. import java.io.FileOutputStream;
  3. import java.io.PrintStream;
  4. import java.text.SimpleDateFormat;
  5. import java.util.Date;
  6. public class Logger {
  7. public static void log(String msg){
  8. try {
  9. //指向一个日志文件
  10. PrintStream out=new PrintStream(new FileOutputStream("log.txt",true));
  11. //改变输出方向
  12. System.setOut(out);
  13. //日期当前时间
  14. Date nowTime = new Date();
  15. SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss: SSS");
  16. String strTime =sdf.format(nowTime);
  17. System.out.println(strTime+":"+msg);
  18. } catch (FileNotFoundException e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. }
  1. public class LogTest {
  2. public static void main(String[] args) {
  3. //测试工具类
  4. Logger.log("调用了System类的gc方法,建议启动垃圾回收");
  5. Logger.log("调用了UserService的doSome()方法");
  6. Logger.log("用户尝试登陆,验证失败");
  7. Logger.log("我非常喜欢这个记录工具哦");
  8. }
  9. }
  1. 2021-11-10 17:12:04: 764:调用了System类的gc方法,建议启动垃圾回收
  2. 2021-11-10 17:12:04: 797:调用了UserServicedoSome()方法
  3. 2021-11-10 17:12:04: 801:用户尝试登陆,验证失败
  4. 2021-11-10 17:12:04: 801:我非常喜欢这个记录工具哦

7.序列化和反序列化

JAVASE - 图2
1:参与序列化和反序列化的对象,必须实现Serializable接口。
注意:通过源代码发现,Serializable接口只是一个标志接口:
public interface Serializable {
}
2
凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。
这样,以后这个类即使代码修改了,但是版本号不变,java虚拟机会认为是同一个类。
3:
transient关键字表示游离的,不参与序列化。
private transient String name; // name不参与序列化操作!

  1. package com.bjpowernode.java.io;
  2. import java.io.Serializable;
  3. public class Student implements Serializable {
  4. private static final long serialVersionUID = 1L;// java虚拟机识别一个类的时候先通过类名,如果类名一致,再通过序列化版本号。
  5. private int no;
  6. private String name;
  7. public Student() {
  8. }
  9. public Student(int no, String name) {
  10. this.no = no;
  11. this.name = name;
  12. }
  13. public int getNo() {
  14. return no;
  15. }
  16. public void setNo(int no) {
  17. this.no = no;
  18. }
  19. public String getName() {
  20. return name;
  21. }
  22. public void setName(String name) {
  23. this.name = name;
  24. }
  25. @Override
  26. public String toString() {
  27. return "Student{" +
  28. "no=" + no +
  29. ", name='" + name + '\'' +
  30. '}';
  31. }
  32. }
  1. package com.bjpowernode.java.io;
  2. import java.io.FileOutputStream;
  3. import java.io.ObjectOutputStream;
  4. public class ObjectOutputStreamTest01 {
  5. public static void main(String[] args)throws Exception {
  6. //创建java对象
  7. Student s =new Student(111,"zhangsan");
  8. //序列化
  9. ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("students"));
  10. //序列化对象
  11. oos.writeObject(s);
  12. //刷新
  13. oos.flush();
  14. //关闭
  15. oos.close();
  16. }
  17. }

一次性序列多个对象(参考)

  1. /*
  2. 一次序列化多个对象呢?
  3. 可以,可以将对象放到集合当中,序列化集合。
  4. 提示:
  5. 参与序列化的ArrayList集合以及集合中的元素User都需要实现 java.io.Serializable接口。
  6. */
  7. public class ObjectOutputStreamTest02 {
  8. public static void main(String[] args) throws Exception{
  9. List<User> userList = new ArrayList<>();
  10. userList.add(new User(1,"zhangsan"));
  11. userList.add(new User(2, "lisi"));
  12. userList.add(new User(3, "wangwu"));
  13. ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("users"));
  14. // 序列化一个集合,这个集合对象中放了很多其他对象。
  15. oos.writeObject(userList);
  16. oos.flush();
  17. oos.close();
  18. }
  19. }

8.IO流和Properties的使用

userinfo.properties

  1. username=admin
  2. password=123
  1. import java.io.FileReader;
  2. import java.util.Properties;
  3. public class IoPropertiesTest01 {
  4. public static void main(String[] args) throws Exception {
  5. //新建一个输入流对象
  6. FileReader reader =new FileReader("src/userinfo.properties");
  7. //新建一个Map集合
  8. Properties pro=new Properties();
  9. //调用properties对象的load方法将文件中的数据加载到Map集合中
  10. pro.load(reader);
  11. //通过key来获取value
  12. String username=pro.getProperty("username");
  13. System.out.println(username);
  14. String password=pro.getProperty("password");
  15. System.out.println(password);
  16. }
  17. }

三、File类常用方法

Files.exists():检测⽂件路径是否存在。
Files.createFile():创建⽂件。
Files.createDirectory():创建⽂件夹。
Files.delete():删除⼀个⽂件或⽬录。
Files.copy():复制⽂件。
Files.move():移动⽂件。
Files.size():查看⽂件个数。
Files.read():读取⽂件。
Files.write():写⼊⽂件。

十二、JAVA多线程

一、实现线程三种方式

一、未开启线程并发

  1. public class MyThread {
  2. public static void main(String[] args) {
  3. BranchThread t=new BranchThread();
  4. t.run();
  5. for(int i=0;i<=100;i++){
  6. System.out.println("主线程"+i);
  7. }
  8. }
  9. }
  10. class BranchThread extends Thread{
  11. public void run(){
  12. for(int i=0;i<=1000;i++){
  13. System.out.println("分支线程------------->"+i);
  14. }
  15. }
  16. }
  1. 1: 自上而下的顺序
  2. 2:先输出分支线程1-1000,输出完之后再输出
  3. 主线程1-100

JAVASE - 图3

一、第一种方式

实现线程的第一种方式:
编写一个类,直接继承java.lang.Thread,重写run方法。

  1. public class MyThread {
  2. public static void main(String[] args) {
  3. BranchThread t=new BranchThread();
  4. //start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。
  5. t.start();
  6. for(int i=0;i<=1000;i++){
  7. System.out.println("主线程"+i);
  8. }
  9. }
  10. }
  11. class BranchThread extends Thread{
  12. public void run(){
  13. for(int i=0;i<=1000;i++){
  14. System.out.println("分支线程------------->"+i);
  15. }
  16. }
  17. }

JAVASE - 图4

  1. "C:\Program Files\Java\jdk-15.0.1\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2020.3.1\lib\idea_rt.jar=64875:C:\Program Files\JetBrains\IntelliJ IDEA 2020.3.1\bin" -Dfile.encoding=UTF-8 -classpath C:\Users\cao\IdeaProjects\untitled3\out\production\untitled Thread.ThreadTest02
  2. 分支线程--------->0
  3. 主线程0
  4. 分支线程--------->1
  5. 主线程1
  6. 分支线程--------->2
  7. 分支线程--------->3
  8. 主线程2
  9. 分支线程--------->4
  10. 主线程3
  11. 分支线程--------->5
  12. 主线程4
  13. 分支线程--------->6
  14. 主线程5
  15. 分支线程--------->7
  16. 主线程6
  17. 分支线程--------->8
  18. 主线程7
  19. 分支线程--------->9
  20. 主线程8
  21. 分支线程--------->10
  22. 主线程9
  23. 分支线程--------->11
  24. 主线程10
  25. 分支线程--------->12
  26. 主线程11
  27. 主线程12
  28. 分支线程--------->13
  29. 主线程13
  30. 分支线程--------->14
  31. 主线程14
  32. 分支线程--------->15

二、第二种方式

实现线程的第二种方式,编写一个类实现java.lang.Runnable接口。

  1. public class MyThread {
  2. public static void main(String[] args) {
  3. /**
  4. * 创建一个可运行的对象
  5. * BranchThread r= new BranchThread();
  6. * 将可运行的对象封装成一个线程对象
  7. * Thread t=new Thread(r)
  8. */
  9. Thread t = new Thread(new BranchThread());//合并代码
  10. t.start();
  11. for(int i=0;i<=100;i++){
  12. System.out.println("主线程"+i);
  13. }
  14. }
  15. }
  16. class BranchThread implements Runnable{
  17. public void run(){
  18. for(int i=0;i<=100;i++){
  19. System.out.println("分支线程------------->"+i);
  20. }
  21. }
  22. }

采用匿名内部类的方式【第二种变形】

  1. public class MyThread {
  2. public static void main(String[] args) {
  3. //创建线程对象,采用匿名内部类方式。
  4. Thread t = new Thread(new Runnable() {
  5. @Override
  6. public void run() {
  7. for (int i = 0; i <= 100; i++) {
  8. System.out.println("t线程----->" + i);
  9. }
  10. }
  11. });
  12. t.start();
  13. for (int i = 0; i <= 100; i++) {
  14. System.out.println("main线程----->" + i);
  15. }
  16. }
  17. }

三、第三种方式

实现线程的第三种方式:
实现Callable接口
这种方式的优点:可以获取到线程的执行结果。
这种方式的缺点:效率比较低,在获取t线程执行结果的时候,当前线程受阻塞,效率较低。

  1. import java.util.concurrent.Callable;
  2. import java.util.concurrent.FutureTask; // JUC包下的,属于java的并发包,老JDK中没有这个包。新特性。
  3. public class MyThread {
  4. public static void main(String[] args) throws Exception {
  5. // 第一步:创建一个“未来任务类”对象。
  6. // 参数非常重要,需要给一个Callable接口实现类对象。
  7. FutureTask task = new FutureTask(new Callable() {
  8. @Override
  9. public Object call() throws Exception { // call()方法就相当于run方法。只不过这个有返回值
  10. // 线程执行一个任务,执行之后可能会有一个执行结果
  11. // 模拟执行
  12. System.out.println("call method begin");
  13. Thread.sleep(1000 * 10);
  14. System.out.println("call method end!");
  15. int a = 100;
  16. int b = 200;
  17. return a + b; //自动装箱(300结果变成Integer)
  18. }
  19. });
  20. // 创建线程对象
  21. Thread t = new Thread(task);
  22. // 启动线程
  23. t.start();
  24. // 这里是main方法,这是在主线程中。
  25. // 在主线程中,怎么获取t线程的返回结果?
  26. // get()方法的执行会导致“当前线程阻塞”
  27. Object obj = task.get();
  28. System.out.println("线程执行结果:" + obj);
  29. // main方法这里的程序要想执行必须等待get()方法的结束
  30. // 而get()方法可能需要很久。因为get()方法是为了拿另一个线程的执行结果
  31. // 另一个线程执行是需要时间的。
  32. System.out.println("hello world!");
  33. }
  34. }
  1. call method begin
  2. /*等待sleep */
  3. call method end!
  4. /*等待另一个线程结束拿到300*/
  5. 线程执行结果:300
  6. hello world!

二、线程的生命周期

JAVASE - 图5

三、线程的三个基本方法

  1. public class MyThread {
  2. public static void main(String[] args) {
  3. MyThread2 t = new MyThread2();
  4. //获取线程名字
  5. System.out.println(t.getName()); //默认值为 Thread-0
  6. //修改线程名字
  7. t.setName("曹1");
  8. System.out.println(t.getName());//曹1
  9. //获取当前线程对象?
  10. Thread y =Thread.currentThread();
  11. System.out.println(y.getName());//main
  12. t.start();
  13. }
  14. }
  15. class MyThread2 extends Thread{
  16. public void run(){
  17. //获取当前线程对象,t.start();谁启动就是谁
  18. Thread y1= Thread.currentThread();
  19. System.out.println(y1.getName());//曹1
  20. }
  21. }

四、Thread.sleep()

关于Thread.sleep()方法的一个面试题;
1、静态方法:Thread.sleep(1000);
2、参数是毫秒
3、作用:让当前线程进入休眠,进入“阻塞状态”,放弃占有CPU时间片,让给其它线程使用。
这行代码出现在A线程中,A线程就会进入休眠。
这行代码出现在B线程中,B线程就会进入休眠。
4、Thread.sleep()方法,可以做到这种效果:
间隔特定的时间,去执行一段特定的代码,每隔多久执行一次

  1. public class MyThread {
  2. public static void main(String[] args) {
  3. //创建一个线程对象,一个多态
  4. Thread t =new MyThread3();
  5. t.setName("t");
  6. t.start();
  7. try {//问题:这行代码会让线程t进入休眠状态吗?
  8. t.sleep(1000*3);//在执行的时候还是会转成:Thread.sleep(1000*5) 这是静态方法
  9. //这行代码的作用是:让当前线程进入休眠,也就是说main进入休眠.
  10. //这样代码出现在main方法中,main线程睡眠。
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. //3秒之后执行到这里
  15. System.out.println("hello word");
  16. }
  17. }
  18. class MyThread3 extends Thread{
  19. public void run(){
  20. for(int i =0;i<10000;i++){
  21. System.out.println(Thread.currentThread().getName()+"------>"+i);
  22. }
  23. }
  24. }

4.1合理终止一个线程

  1. public class MyThread {
  2. public static void main(String[] args) {
  3. MyThread4 r =new MyThread4();
  4. Thread t =new Thread(r);
  5. t.setName("t");
  6. t.start();
  7. //模拟5秒
  8. try {
  9. Thread.sleep(5000);
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }//终止线程
  13. //你想要什么时候终止t的执行,那么你可以把标记修改为false,就结束了。
  14. r.run=false;
  15. }
  16. }
  17. class MyThread4 implements Runnable{
  18. //打一个布尔标记
  19. boolean run=true;
  20. public void run(){
  21. for(int i =0;i<10;i++){
  22. if(run){
  23. System.out.println(Thread.currentThread().getName()+"--->"+i);
  24. try {
  25. Thread.sleep(1000);
  26. } catch (InterruptedException e) {
  27. e.printStackTrace();
  28. }
  29. }else {
  30. //return就结束了,你在结束之前还有什么没保存的。在这里可以保存
  31. //终止当前线程
  32. return;
  33. }
  34. }
  35. }
  36. }
  1. t--->0
  2. t--->1
  3. t--->2
  4. t--->3
  5. t--->4

五 、线程安全问题

1: 什么时候数据在多线程并发的环境下会存在安全问题呢? 三个条件: 条件1:多线程并发。 条件2:有共享数据。 条件3:共享数据有修改的行为。
满足以上3个条件之后,就会存在线程安全问题。

  1. synchronized有三种写法:
  2. 第一种:同步代码块
  3. 灵活
  4. synchronized(线程共享对象){
  5. 同步代码块;
  6. }
  7. 第二种:在实例方法上使用synchronized
  8. 表示共享对象一定是this
  9. 并且同步代码块是整个方法体。
  10. 第三种:在静态方法上使用synchronized
  11. 表示找类锁。
  12. 类锁永远只有1把。
  13. 就算创建了100个对象,那类锁也只有一把。
  14. 对象锁:1个对象1把锁,100个对象100把锁。
  15. 类锁:100个对象,也可能只是1把类锁。

synchronized () 括号中中写什么?
那要看你想让哪些线程同步。
假设t1、t2、t3、t4、t5,有5个线程,
你只希望t1 t2 t3排队,t4 t5不需要排队。怎么办?
你一定要在()中写一个t1 t2 t3共享的对象。而这个
对象对于t4 t5来说不是共享的。
JAVASE - 图6
下面模拟两个人取同一个账户

  1. package ThreadSafe;
  2. public class Account {
  3. private String actno;
  4. private double balance;
  5. public Account() {
  6. }
  7. public Account(String actno, double balance) {
  8. this.actno = actno;
  9. this.balance = balance;
  10. }
  11. public String getActno() {
  12. return actno;
  13. }
  14. public void setActno(String actno) {
  15. this.actno = actno;
  16. }
  17. public double getBalance() {
  18. return balance;
  19. }
  20. public void setBalance(double balance) {
  21. this.balance = balance;
  22. }
  23. //取款的方法 第一种方式
  24. public void withdraw(double money) {
  25. synchronized (this) {
  26. //取款之前户余额
  27. double before = this.getBalance();
  28. //取款之后的余额
  29. double after = before - money;
  30. try {
  31. Thread.sleep(1000);
  32. } catch (InterruptedException e) {
  33. e.printStackTrace();
  34. }
  35. //更新余额
  36. this.setBalance(after);
  37. }
  38. }
  39. }
  40. /* 第二种方式
  41. public synchronized void withdraw(double money){
  42. double before = this.getBalance(); // 10000
  43. double after = before - money;
  44. try {
  45. Thread.sleep(1000);
  46. } catch (InterruptedException e) {
  47. e.printStackTrace();
  48. }
  49. this.setBalance(after);
  50. }
  51. } */
  1. package ThreadSafe;
  2. public class AccountThread extends Thread {
  3. // 两个线程必须共享同一个账户对象。
  4. private Account act;
  5. // 通过构造方法传递过来账户对象
  6. public AccountThread(Account act) {
  7. this.act = act;
  8. }
  9. public void run(){
  10. // run方法的执行表示取款操作。
  11. // 假设取款5000
  12. double money = 5000;
  13. // 取款
  14. // 多线程并发执行这个方法。
  15. act.withdraw(money);
  16. //第三种方式
  17. /* synchronized (act) { // 这种方式也可以,只不过扩大了同步的范围,效率更低了。
  18. act.withdraw(money);
  19. }*/
  20. System.out.println(Thread.currentThread().getName() + "对"+act.getActno()+"取款"+money+"成功,余额" + act.getBalance());
  21. }
  22. }
  1. package ThreadSafe;
  2. public class Test {
  3. public static void main(String[] args) {
  4. // 创建账户对象(只创建1个)
  5. Account act = new Account("act-001", 10000);
  6. // 创建两个线程
  7. Thread t1 = new AccountThread(act);
  8. Thread t2 = new AccountThread(act);
  9. // 设置name
  10. t1.setName("t1");
  11. t2.setName("t2");
  12. // 启动线程取款
  13. t1.start();
  14. t2.start();
  15. }
  16. }

六、死锁

JAVASE - 图7

  1. 死锁
  2. package com.bjpowernode.java.deadlock;
  3. /*
  4. 死锁代码要会写。
  5. 一般面试官要求你会写。
  6. 只有会写的,才会在以后的开发中注意这个事儿。
  7. 因为死锁很难调试。
  8. */
  9. public class DeadLock {
  10. public static void main(String[] args) {
  11. Object o1 = new Object();
  12. Object o2 = new Object();
  13. // t1和t2两个线程共享o1,o2
  14. Thread t1 = new MyThread1(o1,o2);
  15. Thread t2 = new MyThread2(o1,o2);
  16. t1.start();
  17. t2.start();
  18. }
  19. }
  20. class MyThread1 extends Thread{
  21. Object o1;
  22. Object o2;
  23. public MyThread1(Object o1,Object o2){
  24. this.o1 = o1;
  25. this.o2 = o2;
  26. }
  27. public void run(){
  28. synchronized (o1){
  29. try {
  30. Thread.sleep(1000);
  31. } catch (InterruptedException e) {
  32. e.printStackTrace();
  33. }
  34. synchronized (o2){
  35. }
  36. }
  37. }
  38. }
  39. class MyThread2 extends Thread {
  40. Object o1;
  41. Object o2;
  42. public MyThread2(Object o1,Object o2){
  43. this.o1 = o1;
  44. this.o2 = o2;
  45. }
  46. public void run(){
  47. synchronized (o2){
  48. try {
  49. Thread.sleep(1000);
  50. } catch (InterruptedException e) {
  51. e.printStackTrace();
  52. }
  53. synchronized (o1){
  54. }
  55. }
  56. }
  57. }

七、守护线程

  1. public class ThreadTest14 {
  2. public static void main(String[] args) {
  3. Thread t = new BakDataThread();
  4. t.setName("备份数据线程");
  5. //启动线程之前,将线程设置为守护线程
  6. t.setDaemon(true);//守护线程
  7. t.start();
  8. //主线程,主线是用户线程
  9. for(int i=0;i<10;i++){
  10. System.out.println(Thread.currentThread().getName()+"--->"+i);
  11. try {
  12. Thread.sleep(1000);
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. }
  17. }
  18. }
  19. class BakDataThread extends Thread{
  20. public void run(){
  21. int i=0;
  22. //即使是死循环,但由于该线程是守护线程,当用户线程结束,守护线程自动停止。
  23. while (true){
  24. System.out.println(Thread.currentThread().getName()+"--->"+(++i));
  25. try {
  26. Thread.sleep(1000);
  27. } catch (InterruptedException e) {
  28. e.printStackTrace();
  29. }
  30. }
  31. }
  32. }

7.1实现定时器

  1. package com.bjpowernode.java.thread;
  2. import java.text.SimpleDateFormat;
  3. import java.util.Date;
  4. import java.util.Timer;
  5. import java.util.TimerTask;
  6. /*
  7. 使用定时器指定定时任务。
  8. */
  9. public class TimerTest {
  10. public static void main(String[] args) throws Exception {
  11. // 创建定时器对象
  12. Timer timer = new Timer();
  13. //Timer timer = new Timer(true); //守护线程的方式
  14. // 指定定时任务
  15. //timer.schedule(定时任务, 第一次执行时间, 间隔多久执行一次);
  16. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  17. Date firstTime = sdf.parse("2020-03-14 09:34:30");
  18. //timer.schedule(new LogTimerTask() , firstTime, 1000 * 10);
  19. // 每年执行一次。
  20. //timer.schedule(new LogTimerTask() , firstTime, 1000 * 60 * 60 * 24 * 365);
  21. //匿名内部类方式
  22. timer.schedule(new TimerTask(){
  23. @Override
  24. public void run() {
  25. // code....
  26. }
  27. } , firstTime, 1000 * 10);
  28. }
  29. }
  30. // 编写一个定时任务类
  31. // 假设这是一个记录日志的定时任务
  32. class LogTimerTask extends TimerTask {
  33. @Override
  34. public void run() {
  35. // 编写你需要执行的任务就行了。
  36. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  37. String strTime = sdf.format(new Date());
  38. System.out.println(strTime + ":成功完成了一次数据备份!");
  39. }

八、wait和notify方法

1.注意:wait和notify方法不是线程对象的方法,是java中任何一个java对象
都有的方法,因为这两个方式是Object类中自带的。

Object o = new Object();
o.wait();
2.wait()方法的作用:o.wait()让正在o对象上活动的线程t进入等待状态,并且释放掉t线程之前占有的o对象的锁。
3、notify方法作用:o.notify()让正在o对象上等待的线程唤醒,只是通知,不会释放o对象上之前占有的锁。
4.notifyAll()方法这个方法是唤醒o对象上处于等待的所有线程。

下面模拟生产者与消费者关系

  1. package bjpowernode;
  2. public class Store {
  3. private int mike;
  4. //默认仓库是没有牛奶的
  5. boolean state=false;
  6. public synchronized void put(int mike){
  7. //如果state为true,则说明有牛奶需要让该线程等待
  8. if(state){
  9. try {
  10. wait();
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. //state为fasle,没有牛奶可以生产
  16. this.mike=mike;
  17. System.out.println("将第几"+this.mike+"个牛奶放入仓库");
  18. //等生产完了标记为true
  19. state=true;
  20. //唤醒等待中所有线程
  21. notifyAll();
  22. /*注意唤醒之后由于state为true,所以返回到上面释放掉该线程重新抢*/
  23. }
  24. public synchronized void get(){
  25. //从上面wait过来的,所以state为true
  26. //!state表示没有牛奶
  27. if(!state){
  28. try {
  29. wait();
  30. } catch (InterruptedException e) {
  31. e.printStackTrace();
  32. }
  33. }
  34. System.out.println("取得第"+this.mike+"个牛奶");
  35. //取完牛奶后就没了,标记为false
  36. state =false;
  37. notifyAll();
  38. }
  39. }
  1. package bjpowernode;
  2. public class Put implements Runnable {
  3. Store store;
  4. public Put(Store store){
  5. this.store=store;
  6. }
  7. @Override
  8. public void run() {
  9. for(int i=1;i<6;i++){
  10. store.put(i);
  11. }
  12. }
  13. }
  1. package bjpowernode;
  2. public class Get implements Runnable {
  3. Store store;
  4. public Get(Store store){
  5. this.store=store;
  6. }
  7. @Override
  8. public void run() {
  9. while (true) {
  10. store.get();
  11. }
  12. }
  13. }
  1. package bjpowernode;
  2. public class Test {
  3. public static void main(String[] args) {
  4. Store store = new Store();
  5. Put put = new Put(store);
  6. Get get = new Get(store);
  7. Thread t1 = new Thread(put);
  8. Thread t2 =new Thread(get);
  9. t1.setName("t1");
  10. t1.setName("t2");
  11. t1.start();
  12. t2.start();
  13. }
  14. }

十三、注解

Java 注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。

一、注解的用处:

  1. 1、生成文档。这是最常见的,也是java 最早提供的注解。常用的有@param @return 等<br /> 2、跟踪代码依赖性,实现替代配置文件功能。比如Dagger 2 依赖注入,未来java 开发,将大量注解配置,具有很大用处;<br /> 3、在编译时进行格式检查。如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出

二、注解的原理:

注解本质是一个继承了Annotation 的特殊接口,其具体实现类是Java 运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java 运行时生成的动态代理对象$Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler 的invoke 方法。该方法会从memberValues 这个Map 中索引出对应的值。而memberValues 的来源是Java 常量池。

三、元注解

java.lang.annotation 提供了四种元注解,专门注解其他的注解(在自定义注解的时候,需要使用到元注解):
@Documented – 注解是否将包含在JavaDoc中
@Retention – 什么时候使用该注解
@Target – 注解用于什么地方
@Inherited – 是否允许子类继承该注解
1.)@Retention – 定义该注解的生命周期
● RetentionPolicy.SOURCE : 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。
● RetentionPolicy.CLASS : 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式
● RetentionPolicy.RUNTIME : 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
2.)Target – 表示该注解用于什么地方。默认值为任何元素,表示该注解用于什么地方。可用的ElementType 参数包括
● ElementType.CONSTRUCTOR: 用于描述构造器
● ElementType.FIELD: 成员变量、对象、属性(包括enum实例)
● ElementType.LOCAL_VARIABLE: 用于描述局部变量
● ElementType.METHOD: 用于描述方法
● ElementType.PACKAGE: 用于描述包
● ElementType.PARAMETER: 用于描述参数
● ElementType.TYPE: 用于描述类、接口(包括注解类型) 或enum声明
3.)@Documented – 一个简单的Annotations 标记注解,表示是否将注解信息添加在java 文档中。
4.)@Inherited – 定义该注释和子类的关系
@Inherited 元注解是一个标记注解,@Inherited 阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited 修饰的annotation 类型被用于一个class,则这个annotation 将被用于该class 的子类。

四、常见标准Annotation

1.)Override
java.lang.Override 是一个标记类型注解,它被用作标注方法。它说明了被标注的方法重写了父类的方法,起到了断言的作用。如果我们使用了这种注解在一个没有覆盖父类方法的方法时,java 编译器将以一个编译错误来警示。
2.)Deprecated
Deprecated 也是一种标记类型注解。当一个类型或者类型成员使用@Deprecated 修饰的话,编译器将不鼓励使用这个被标注的程序元素。所以使用这种修饰具有一定的“延续性”:如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为@Deprecated,但编译器仍然要报警。
3.)SuppressWarnings
SuppressWarning 不是一个标记类型注解。它有一个类型为String[] 的成员,这个成员的值为被禁止的警告名。对于javac 编译器来讲,被-Xlint 选项有效的警告名也同样对@SuppressWarings 有效,同时编译器忽略掉无法识别的警告名。
@SuppressWarnings(“unchecked”)

五、自定义注解

自定义注解类编写的一些规则:
1. Annotation 型定义为@interface, 所有的Annotation 会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口.
2. 参数成员只能用public 或默认(default) 这两个访问权修饰
3. 参数成员只能用基本类型byte、short、char、int、long、float、double、boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组.
4. 要获取类方法和字段的注解信息,必须通过Java的反射技术来获取 Annotation 对象,因为你除此之外没有别的获取注解对象的方法
5. 注解也可以没有定义成员,,不过这样注解就没啥用了
PS:自定义注解需要使用到元注解

六、自定义注解的实例:

FruitName.java

  1. import java.lang.annotation.Documented;
  2. import java.lang.annotation.Retention;
  3. import java.lang.annotation.Target;
  4. import static java.lang.annotation.ElementType.FIELD;
  5. import static java.lang.annotation.RetentionPolicy.RUNTIME;
  6. /**
  7. * 水果名称注解
  8. */
  9. @Target(FIELD)
  10. @Retention(RUNTIME)
  11. @Documented
  12. public @interface FruitName {
  13. /* 属性 属性默认值*/
  14. String value() default "";
  15. }

FruitColor.java

  1. import java.lang.annotation.Documented;
  2. import java.lang.annotation.Retention;
  3. import java.lang.annotation.Target;
  4. import static java.lang.annotation.ElementType.FIELD;
  5. import static java.lang.annotation.RetentionPolicy.RUNTIME;
  6. /**
  7. * 水果颜色注解
  8. */
  9. @Target(FIELD)
  10. @Retention(RUNTIME)
  11. @Documented
  12. public @interface FruitColor {
  13. /**
  14. * 颜色枚举
  15. */
  16. public enum Color{ BLUE,RED,GREEN};
  17. /**
  18. * 颜色属性
  19. */
  20. Color fruitColor() default Color.GREEN;
  21. }

FruitProvider.java

  1. import java.lang.annotation.Documented;
  2. import java.lang.annotation.Retention;
  3. import java.lang.annotation.Target;
  4. import static java.lang.annotation.ElementType.FIELD;
  5. import static java.lang.annotation.RetentionPolicy.RUNTIME;
  6. /**
  7. * 水果供应者注解
  8. */
  9. @Target(FIELD)
  10. @Retention(RUNTIME)
  11. @Documented
  12. public @interface FruitProvider {
  13. /**
  14. * 供应商编号
  15. */
  16. public int id() default -1;
  17. /**
  18. * 供应商名称
  19. */
  20. public String name() default "";
  21. /**
  22. * 供应商地址
  23. */
  24. public String address() default "";
  25. }

FruitInfoUtil.java

  1. import java.lang.reflect.Field;
  2. /**
  3. * 注解处理器
  4. */
  5. public class FruitInfoUtil {
  6. public static void getFruitInfo(Class<?> clazz){
  7. String strFruitName=" 水果名称:";
  8. String strFruitColor=" 水果颜色:";
  9. String strFruitProvicer="供应商信息:";
  10. Field[] fields = clazz.getDeclaredFields();
  11. for(Field field :fields){
  12. //A.isAnnotationPresent(B.class);意思就是:注释B是否在此A上。
  13. if(field.isAnnotationPresent(FruitName.class)){
  14. //获取该注解对象
  15. FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
  16. //获取该注解的属性ruitName.value();
  17. strFruitName=strFruitName+fruitName.value();
  18. System.out.println(strFruitName);
  19. }
  20. else if(field.isAnnotationPresent(FruitColor.class)){
  21. FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
  22. strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
  23. System.out.println(strFruitColor);
  24. }
  25. else if(field.isAnnotationPresent(FruitProvider.class)){
  26. FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
  27. strFruitProvicer=" 供应商编号:"+fruitProvider.id()+" 供应商名称:"+fruitProvider.name()+" 供应商地址:"+fruitProvider.address();
  28. System.out.println(strFruitProvicer);
  29. }
  30. }
  31. }
  32. }

Apple.java

  1. import test.FruitColor.Color;
  2. /**
  3. * 注解使用
  4. */
  5. public class Apple {
  6. @FruitName("Apple")
  7. private String appleName;
  8. @FruitColor(fruitColor=Color.RED)
  9. private String appleColor;
  10. @FruitProvider(id=1,name="陕西红富士集团",address="陕西省西安市延安路89号红富士大厦")
  11. private String appleProvider;
  12. public void setAppleColor(String appleColor) {
  13. this.appleColor = appleColor;
  14. }
  15. public String getAppleColor() {
  16. return appleColor;
  17. }
  18. public void setAppleName(String appleName) {
  19. this.appleName = appleName;
  20. }
  21. public String getAppleName() {
  22. return appleName;
  23. }
  24. public void setAppleProvider(String appleProvider) {
  25. this.appleProvider = appleProvider;
  26. }
  27. public String getAppleProvider() {
  28. return appleProvider;
  29. }
  30. public void displayName(){
  31. System.out.println("水果的名字是:苹果");
  32. }
  33. }

FruitRun.java

  1. /**
  2. * 输出结果
  3. */
  4. public class FruitRun {
  5. public static void main(String[] args) {
  6. FruitInfoUtil.getFruitInfo(Apple.class);
  7. }
  8. }

运行结果是:

水果名称:Apple
水果颜色:RED
供应商编号:1 供应商名称:陕西红富士集团 供应商地址:陕西省西安市延安路89号红富士大厦