JAVA基础笔记合集(类+Stream流+异常处理+日志+文件IO+线程)

JAVA Basic

· 入门

在这里插入图片描述
java11之后不单独提供JRE

编译运行步骤 字节码 机器码
在这里插入图片描述
不同操作系统的机器码不通用,所以中间借用字节码

· 类设计

成员变量+成员方法+构造器+代码块+内部类

Java文件定义多个类中只能有一个由public修饰,且该类名必须是文件名

在这里插入图片描述

  1. this的使用

    this用来在成员方法中访问类域,不同名时无所谓

class Point{
	int x,y;  //域
	Point()
	{
		x=0:
		y=0;//this.x,this.y也可
	}
	Point(int x,int y)
	{
		this.x=x;
		this.y=y;  //必须写,不然不能区分参数和类域
	}
}
  1. 对象数组

    数组中元素使用自己创造的对象,而不是常规的INT等变量

    class Point{...}
    public class example{
    	public static void main(String args[])
    	{
    		Point p1 = new Point(10,11);
    		Point p2 = new Point(20,21);
    		Point p3 = new Point(30,31);
    		//初始化的多种方法
    		Point point1 = {p1,p2,p3,new Point(40,41)};
    	}
    }
    

    域中定义变量 static int才能发挥全局作用

  2. 访问权限 (封装Encapsulation)

    Java的封装可以使用保护修饰词,有private, protected, public

    访问权限本类本包的类子类非子类的外包类
    public
    protected
    default
    private
  3. 类的继承

    class father
    {
    	//无参构造器
    	father(){...}
    	//有参构造器
    	father(a,b,c){...}
    }
    class son extends father
    {
    	son(){...}
    	son(a,b,c)
    	{
    		//先初始化父类构造器,不写也是隐式调用无参的,如要有参必须写
    		super();
    		//再构造自己
    		this.name = name;
    	}
    	//子类域
    	//子类方法
    }
    main:
    	son a = new son();
    	//先执行父类无参构造器,再执行子类相应(有参或无参构造器)!!
    

    子类可以继承父类的域和方法,就当作自身定义的一样使用

    子类可能不适用父类的域或方法,重新定义父类的域称为域的隐藏,重新定义继承自父类的方法称为方法的重写

    域的隐藏:在子类域中取域父类域相同名称的变量

    方法的重写:在子类方法中写相同名称的函数

    super关键字
    若子类隐藏了父类的域,或重写了父类的方法,但仍想引用这些方法,可通过super关键字访问

    super.super.方法([para])
    //调用父类的构造方法
    super([para])
    

    Object类(类的大爹)
    我们创建一个类时,如果没有明确继承一个父类,那么它就会自动继承 Object,成为 Object 的子类。
    Object类中有很多方法可以用以重写,如clone、equals、toString…
    https://blog.csdn.net/weixin_43232955/article/details/89705350

    final关键字标记的域或方法不能被重写
    public static final double PI = 3.1415926
    public final int getA()

    @Override注解

    它标注的方法必须是对父类的重写,若父类没有则报错

  4. 组合类&派生类

    https://www.cnblogs.com/wft1990/p/5995416.html

  5. 类的多态

    多态指的是同一类对象的不同行为,eg.飞机&汽车都属于交通工具类,但运行方式不同

    多态更强调行为的多态,而不是变量的多态

    赋值兼容规则

    类Circle是类Point1的子类,则可以有Point p = new Circle(15,25,10);称p(父类对象)为子类对象的上转型对象

    多态的优势

    1.  Animal a = new Dog()
       Animal a = new Tiger()
       //多态使得右边对象可以解耦合,便于扩展和维护
      

    2. 定义方法的时候用父类型作为参数,所有子类型都可以传参

    多态的劣势

    1. 多态下不能访问子类的独有功能—》sol. 引用数据类型的类型转换

    引用数据类型的类型转换

    1. 自动类型转换(子👉父)

    2. 强制类型转换(父👉子)

      Animal a = new Tiger();
      //此时不能调用Tiger类独有function
      Tiger t = (Tiger)a;
      //强制类型转换后可以调用独有功能
      //!下面写法是运行时报错的
      Dog d = (Dog)a;//上面new的是Tiger,不能转Dog,异常:ClassCastException
      

      因此,JAVA推荐在强转前用instanceof判断对象真实类型再进行强转

      if(t instanceof Tiger){//强转}

  1. 抽象类/方法&接口

    abstract class Shape//抽象类
    {
    	public abstract double area();//抽象方法
    	public abstract double perimeter();//抽象方法
    }
    //重写抽象类的方法以适应子类特征(必须重写抽象方法)
    class Circle extends Shape{...}
    class Rectangle extends Shape{...}
    public class example{
    	public static void main(String args[]){
    		Shape aShape;声明抽象类的对象
    		//aShape = new Shape是不被允许的,即实例化抽象类是不允许的
    		aShape = new Circle(10);
    		aShape = new Rectangle(15.2,10.8);
    	}
    }
    

    接口可看作特殊的抽象类,但interface与class不同,class是对一类事物的描述,而interface可以描述不同类型的事物

    接口的域只能是不可重新赋值的常量;接口只能声明方法,不能定义方法

    interface Shape
    {
    	public final static double PI = 3.1415926;
    	//interface中必须100%的抽象类
    	public abstract double area();
    	public abstract double perimeter();
    }
    class Circle implements Shape{
    	//构造函数
    	//接口方法实现的函数
    }
    

  1. static关键字的使用

    静态成员变量
    static int a = 0可以用 类名.a 的方式访问

    实例成员变量
    private int b = 1 只能用实例化后的对象访问,即类名 c = new 类名();c.b = 1

    在这里插入图片描述

    静态成员方法
    属于类,public static int get(){...},共享调用

    实例方法
    属于实例,public void get(){...},实例调用

  1. 代码块

    //用以初始化
    {...}//实例代码块
    static {...}//静态代码块
    

  1. 内部类(lambda表达式)

    静态内部类、成员内部类、匿名内部类

    //静态内部类
    public class Outer{
    	public static class inner{...}
    }
    //成员内部类
    public class Outer{
    	public class inner{...}
    }
    

    匿名内部类:方便创建子类对象,简化代码的书写 (可以将构造器作为参数直接传入方法)

    Employee a = new Employee(){
    	public void work(){...}
    };
    a.work();
    

    Lambda表达式简化匿名内部类的代码写法!!
    (只能简化函数式接口的匿名内部类的写法)

    函数式接口:接口中只有一个抽象方法的形式 @FunctionalInterface

    //Lambda的格式
    //(匿名内部类被重写方法的形参列表) -> {
    //	重写的方法体;
    //}
    
    //简化Arrays.sort Comparator接口
    public class Lambda {
        public static void main(String[] args) {
            Integer[] ages = {34,12,42,23};
            Arrays.sort(ages, new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return o1-o2;//升序
                }
            });
        }
    }
    简化为👇
    public class Lambda {
        public static void main(String[] args) {
            Integer[] ages = {34,12,42,23};
            Arrays.sort(ages, (o1, o2) -> {
                return o1-o2;//升序
            });
        }
    }
    

    在这里插入图片描述

  2. 可变参数

    public static void main(String args[]){
    	sum(10);
    	sum(10,20)
    	...//都是合理的
    }
    //传入就是一个数组
    public static void sum(int ... sum){...}
    

· 核心类库(API)

  1. String类

    使用构造器创建 new String() 👉 堆内存 👉 不同地址

    使用“”创建 👉 常量池 👉 相同地址

    String类API

    和长度有关的方法
    返回类型 方法名 作用
    int length() 得到一个字符串的字符个数(一个中文是一个字符,一个英文是一个字符,一个转义字符是一个字符)

    和数组有关的方法
    返回类型 方法名 作用
    byte[] getBytes() 将一个字符串转换成字节数组
    char[] toCharArray() 将一个字符串转换成字符数组
    String[] split(String) 将一个字符串按照指定内容劈开

    和判断有关的方法
    返回类型 方法名 作用
    boolean equals(String) 判断两个字符串的内容是否一模一样
    boolean equalsIgnoreCase(String) 忽略大小写的比较两个字符串的内容是否一模一样
    boolean contains(String) 判断一个字符串里面是否包含指定的内容
    boolean startsWith(String) 判断一个字符串是否以指定的内容开头
    boolean endsWith(String) 判断一个字符串是否以指定的内容结尾

    和改变内容有关的方法
    和改变内容有关的方法,都不会直接操作原本的字符串
    而是将符合条件的字符串返回给我们,所以注意接收

    返回类型 方法名 作用
    String toUpperCase() 将一个字符串全部转换成大写
    String toLowerCase() 将一个字符串全部转换成小写
    String replace(String,String) 将某个内容全部替换成指定内容
    String replaceAll(String,String) 将某个内容全部替换成指定内容,支持正则
    String repalceFirst(String,String) 将第一次出现的某个内容替换成指定的内容
    String substring(int) 从指定下标开始一直截取到字符串的最后
    String substring(int,int) 从下标x截取到下标y-1对应的元素
    String trim() 去除一个字符串的前后空格

    和位置有关的方法
    返回类型 方法名 作用
    char charAt(int) 得到指定下标位置对应的字符
    int indexOf(String) 得到指定内容第一次出现的下标
    int lastIndexOf(String) 得到指定内容最后一次出现的下标

  2. Object类

    1. toString​**()**

      返回当前对象的地址
      需要自己重写方法来实现具体功能

    2. equals()

      判断对象是否相等(基本类型&String-compare value;引用类型-compare LOC),instanceof/强转等

      重写后一般连着hashcode()一块重写,因为equals不再具有’=='的从地址判断的功能,而两个对象hashcode相同
      对象并不一定相同(也就是hashcode相同,equals的结果可能仍不同)

  3. Objects类

    继承于Object类,Objects.equals(a,b)写法更安全(null不报错)

  4. StringBuilder&&StringBuffer类

    可变string类,可看作对象容器,好处就是不用创建新的string对象

    StringBuilder a = new StringBuilder(),append()、reverse()、length()

    1. 字符修改上的区别

      StringBuilder的修改效率高,但非线程安全(不能同步访问);StringBuffer恰恰相反

    2. 继承结构

      在这里插入图片描述

    3. 缓冲区

      StringBuffer 每次获取 toString 都会直接使用缓存区的 toStringCache 值来构造一个字符串。

      而 StringBuilder 则每次都需要复制一次字符数组,再构造一个字符串。

  5. Math类

    都是静态方法,可以通过类名直接调用

    在这里插入图片描述

  6. System类

    在这里插入图片描述

  7. BigDecimal类(大数据类型)

    解决浮点型运算精度失真

    调用 **.valueof()**方法获取BD对象

  8. 日期类

    1. Date类

      时间毫秒值可以用来方便计算日期对象
      System.currentTimeMills()获取系统时间毫秒值
      Date d = new Date(时间毫秒值对象)ord.setTime(时间毫秒值对象)可以将时间毫秒值转为日期对象

      在这里插入图片描述

    2. SimpleDateFormat类

      Date对象时间毫秒值格式化年月日时间的时间形式

      在这里插入图片描述

      字符串时间形式解析成日期对象
      public Date parse(String source)

      https://blog.csdn.net/qq_26817225/article/details/93593989 对于年代标识符的使用

    3. Calendar类(抽象类,不能直接创建对象)

      在这里插入图片描述

    4. JDK8的新增日期API

      在这里插入图片描述

      LocalDate、LocalTime、LocalDateTimeof()设定时间,now()获取当前时间…

      Instant: 时间戳,类似System.currentTimeMillis()
      .now()方法默认获取世界标准时间,不是东八区时间
      Date与Instant对象互转,Date.from(Instant),Date.toInstant()

      DateTimeFormatter

      格式化format:
      在这里插入图片描述

      解析parse:
      在这里插入图片描述

      Duration || Period:计算时间跨度

      Period对应日期操作,Duration对应时间操作

      Period p = Period.between(a,b)//a,b都是LocalDate对象,默认用b-a
      //Duration用于更精细的操作
      LocalDateTime today = LocalDateTime.now();
      LocalDateTime birth = LocalDateTime.of(2001,11,25,0,5,0);
      Duration duration = Duration.between(birth,today);
      

      可以用ChronoUnit简化

      LocalDateTime today = LocalDateTime.now();
      LocalDateTime birth = LocalDateTime.of(2001,11,25,0,5,0);
      ChronoUnit.YEARS.between(birth,today).sout;
      
  9. 包装类

    在这里插入图片描述
    在这里插入图片描述

    Parsexxx可以用valueOf替代

  10. Arrays类

    对所有数组类型可以操作

    toString、sort、binarySearch等

    Comparater接口用来制定比较器规则(当sort对象为自定义类对象时非常好用)

    Array.sort(array, new Comparater<Student>(){
    	@override
    	public int compare(Student o1, Student o2){
    		//制定比较规则
    		return o2.getAge()-o1.getAge();//年龄降序排序
    	}
    })
    
  11. Java集合(Collection)框架​==、Map框架==

    在这里插入图片描述

    在这里插入图片描述
    框架统一于util包内

    Collection集合的遍历:迭代器Iterator,foreach,lambda表达式(Collection不支持索引)

    List集合:查询快,线性表(有序,有索引,可重复)
    Set集合:增删改查都快,不重复集合
    Map集合:键值对
    Queue集合:队列

    在这里插入图片描述

    Collection特点:集合大小不固定,可以动态变化,类型也可以不固定(对基本数据类型的引用只能用包装类),非常适合做增删操作

    1. List集合

      ArrayList列表、LinkedList链表
      Vecto****r与ArrayList原理相同,但使用了synchronized方法保证线程安全,因此性能略差
      Stack继承自Vector,拓展了push、pop、peek(取堆栈顶点)、empty、search操作以实现后进先出的堆栈结构

      ArrayList构造方法

      方法名方法功能
      ArrayList()构造一个初始容量10的空列表
      ArrayList(Collection c)构造一个包含指定collection的元素的列表
      ArrayList(int capacity)构造一个指定初始容量的空列表

      List接口常用方法(Arraylist同用)
      void add(int index, Object obj)
      Inserts obj 插入到调用列表中的索引通过索引处。达到或超出插入点任何预先存在的要素被上移。因此,不会有元素被覆盖。
      boolean addAll(int index, Collection c)
      插入c的所有元素入索引通过索引处的调用列表。等于或超出插入点任何预先存在的要素被上移。因此,没有任何元素被覆盖。如果调用列表更改并返回true,否则返回false。
      Object get(int index)
      返回存储调用集合中指定索引处的对象。
      int indexOf(Object obj)
      返回调用列表obj的第一个实例的索引。如果obj不是列表中的一个元素,则返回-1。
      int lastIndexOf(Object obj)
      返回调用列表obj的最后一个实例的索引。如果obj不是列表中的一个元素,则返回-1。
      ListIterator listIterator( )
      返回一个迭代器调用列表的开始。
      List Iterator list Iterator(int index)
      返回一个迭代器调用列表开头的在指定索引处。
      Object remove(int index)
      从调用列表删除index位置的元素,并返回被删除的元素。结果列表中被压缩。也就是说,随后的元素的索引减一。
      Object set(int index, Object obj)
      赋予obj转换通过索引调用列表中指定的位置。
      List subList(int start, int end)
      返回一个列表,其中包括在调用列表,从开始元素end-1。在返回列表中的元素也被调用对象的引用。
    2. 泛型深入
      1. 泛型类

        public class myClass<E>{...}→创建类对象时myClass<包装类> list = new myClass<>()限定对象的成员类型

      2. 泛型方法

        public <T> void show(T t),使用泛型接受一切类型的参数,更具有通用性

      3. 泛型接口

        public interface Data<E>{...}让实现类选择当前功能需要的数据类型

      4. 泛型通配符、上下限

        使用泛型 的时候代表一切类型(ETKV是在定义泛型使用的)

        在这里插入图片描述

    3. Set集合

      在这里插入图片描述

      1. HashSet

        • 无序的底层原理:哈希表(数组+链表+红黑树)

          哈希值:根据对象地址,按某种规则计算得到的int数值,可以直接用Object类下的public int hashCode()获取

          在这里插入图片描述

          Step4的equals()方法和hashCode()方法需要重写

          JDK8以后,如果链表长度超过8,会自动转为红黑树

          在这里插入图片描述

          在这里插入图片描述

        • 去重的底层原理:先判断Hash值,再判断equals(),Override Object类中的hashCode和equals才能正确去重

      2. LinkedHashSet

        在这里插入图片描述

      3. TreeSet

        在这里插入图片描述

        包装类按照大小、首字符编号等排序,自定义对象需要自定义排序规则 👇

        在这里插入图片描述
        在这里插入图片描述

        浮点型比较直接用Double.compare()

    4. Map集合

      键值对集合(双列集合),每个元素有key,value俩值{key1=value1,key2=value2,…}

      特点:HashMap、TreeMap、LinkedHashMap作为重点,key无序不重复(后覆盖前),value可以有多个key。

      • HashMap:无序、不重复、无索引
      • LinkedHashMap:有序、不重复、无索引

      常用API

      map.put("key_1",1);               // 添加键值对,已有 key 则覆盖 value
      map.putIfAbsent("key_2",2);       // 添加键值对,已有 key 则不操作
      
      map.remove("key_1");              // 删除键值对(按值)           
      map.remove("key_2",2);            // 删除键值对(按键值)
      
      map.get("key_1");                 // 获取值, key 不存在返回null
      map.getOrDefault("key_2",-1);     // 获取值, key 不存在返回默认值
      
      map.containsKey("key_1");         // 判断 key 是否存在  
      map.containsValue(1);             // 判断 value 是否存在      
      
      map.keySet();
      map.values();
      

      遍历的3种方式

      • 键找值

        Step1. 拿keySet集合
        Step2. 遍历每个键,用键提取值,value = maps.get(key)

      • 键值对

        Step1. 将Map转化为Set集合,使用Map的实体类型Set<Map.Entry<String,Integer>> entries = maps.entrySet();
        Step2. foreach遍历​ for(Map.Entry<String,Integer> entry : entries){entry.getKey / entry.getValue}

      • Lambda

        利用Map中的 forEach(BiConsumer<? super K,? super V> action) 方法
        实现:maps.foreach((k,v) ->{...})

      1. HashMap

        在这里插入图片描述

      2. TreeMap

        在这里插入图片描述

      3. LinkedHashMap

        在这里插入图片描述

  12. 补充:集合工具类Collections

    不属于Collection家族,是一个辅助工具,具体参照API文档,addAll、shuffle、sort等

    在这里插入图片描述
    在这里插入图片描述

  13. 补充:集合的嵌套

    在这里插入图片描述

· 正则表达式

用规定的字符制定规则,用来校验数据格式的合法性

==API文档regex.pattern==

在这里插入图片描述

String的一些方法(如split、replace)都可以使用正则表达式传参

==正则表达式爬取信息==
public class RegularExpression {
    public static void main(String[] args) {
        String rs = "电影名称:《我和我的祖国》,票价:43元,联系电话:400-100-3233,邮箱地址:xyf@scnu.com" +
                "电影名称:《闯江湖》,票价:63元,联系电话:400-100-2020,邮箱地址:xyf@126.com.cn";
        //爬取规则
        String regex = "(《\\W{1,10}》)|(\\d+元)|(400-?\\d{3,9}-\\d{3,9})|(\\w{1,30}@[a-zA-Z0-9]{2,20}(\\.[a-zA-Z0-9]{2,20}){1,2})";
        //爬取规则编译为匹配对象
        Pattern pattern = Pattern.compile(regex);
        //得到一个内容匹配器对象
        Matcher matcher = pattern.matcher(rs);
        //开始爬取
        while (matcher.find()){
            String rs1 = matcher.group();
            System.out.println(rs1);
        }
    }
}
	OUT{
		《我和我的祖国》
		43400-100-3233
		xyf@scnu.com
		《闯江湖》
		63400-100-2020
		xyf@126.com.cn
	}

· Stream流体系

  1. 不可变集合(list,set,map)

    List<T> lists = list.of(...)不允许add,set操作

  2. Stream流

    目的:结合Lambda表达式,简化集合和数组操作的API(不会影响原集合结构(无增删改))

    在这里插入图片描述
    在这里插入图片描述

    // Map集合获取流
    Map<String, Integer> maps = new HashMap<>();
    //key stream
    Stream<String> a = maps.KeySet().stream();
    //value stream
    Stream<String> b = maps.values().stream();
    //key-value steam
    Stream<Maps.Entry<String,Integer>> c = maps.entrySet().stream();
    
    //数组获取流
    Arrays.stream()
    //或者
    Stream<String> a = Stream.of() 
    
    Stream流的常用API
    方法名称方法作用方法种类是否支持链式调用
    count统计个数终结方法
    forEach逐一处理终结方法
    filter过滤函数拼接
    limit取用前几个函数拼接
    skip跳过前几个函数拼接
    map映射函数拼接
    concat组合函数拼接

    注:终结方法:返回值类型不再是Stream接口本身类型的方法
    非终结方法/延迟方法:返回值类型仍然是Stream接口自身类型的方法,除了终结方法都是延迟方法。

    Stream流 to Collection
            List<String> list2 = new ArrayList<>();
            list2.add("张老三");
            list2.add("张小三");
            list2.add("李四");
            list2.add("赵五");
            list2.add("张六");
            list2.add("王八");
     
            // 需求:过滤出姓张的并且长度为3的元素
            Stream<String> stream = list2.stream().filter(name -> name.startsWith("张")).filter(name -> name.length() == 3);
     
            // stream 收集到单列集合中
            List<String> list = stream.collect(Collectors.toList());
            System.out.println(list);
     
            // stream 手机到单列集合中
            Set<String> set = stream.collect(Collectors.toSet());
            System.out.println(set);
    

· 异常处

在这里插入图片描述

  1. 异常处理方式

    • throwsfunction throws Exceptions{...}

    • try catch :

      try{
      	//需要监视的代码段
      }catch(异常类型1 e){
      	//处理方式
      	e.printStackTrace();
      }catch(异常类型2 e){
      	//处理方式
      }
      //企业级写法
      catch(Exception e){
      	e.printStackTrace();
      }
      
    • 调用者决定 :function将exception throw给上层调用者,上层调用者再做try catch决定如何处理

    RuntimeException可以在最外层try catch

  2. 自定义异常

    1. 自定义编译时异常

      定义一个异常类继承Exception -> 重写构造器 -> 出现异常的地方用throw new抛出

      //出现异常的地方用`throw new`抛出
      public static void main(String arg[]){
      	try{checkAge(-23)}
      	catch(xyfAgeIllegalException e){e.printStackTrace();}
      }
      public static void checkAge(int age) throws xyfAgeIllegalException{
      	if (age<0||age>200){
      		throw new xyfAgeIllegalException(age + "is illegal!");
      	}
      	else{
      		sout("年龄合法!");
      	}
      }
      
      //重写构造器
      public class xyfAgeIllegalException extends Exception{
      	public xyfAgeIllegalException(){}
      	public xyfAgeIllegalException(String message){super(message);}
      }
      
    2. 自定义运行时异常

      定义一个异常类继承RuntimeException -> 重写构造器 -> 出现异常的地方用throw new抛出

      //出现异常的地方用`throw new`抛出
      public static void main(String arg[]){
      	try{checkAge(-23)}
      	catch(xyfAgeIllegalRuntimeException e){e.printStackTrace();}
      }
      public static void checkAge(int age) throws xyfAgeIllegalRuntimeException{
      	if (age<0||age>200){
      		throw new xyfAgeIllegalRuntimeException(age + "is illegal!");
      	}
      	else{
      		sout("年龄合法!");
      	}
      }
      
      //重写构造器
      public class xyfAgeIllegalRuntimeException extends RuntimeException{
      	public xyfAgeIllegalException(){}
      	public xyfAgeIllegalException(String message){super(message);}
      }
      

· 日志技术

**Advantage:**可以将日志信息写入文件或数据库 + 不需修改代码,灵活性好 + 多线程性能较好

  1. Logback框架

    • 三个技术模块
      logback-core:为其他俩模块奠定基础
      logback-classic:Log4j的改良版本,且完整实现了slf4j的API(日志规范)
      logback-access:与Tomcat && Jetty等Servlet容器集成,以提供HTTP访问日志功能

    • Logback入门

      S1. 导入jar包(logback-classic.jar, logback-core.jar, slf4j-api.jar),至新建的lib文件夹下
      S2. logback.xml拷贝到src目录下
      S3. 代码中获取日志的对象public static final logger LOGGER = LoggerFactory.getLogger("类对象");
      S4. 使用日志对象输出日志

    • Logback配置

      通过 logback.xml 配置,可以设置日志输出位置和格式

      日志输出位置和格式设置
      可以设置输出位置(CONSOLE or FILE)和日志信息的详细格式

      <appender name="CONSOLE" class="...">
      	<target>System.out</target> <!--.err打红色 -->
      	<encoder>
      		<!--format输出。%d-日期 %thread-线程名 %-5level-level从左显示5个字符宽度 %msg-日志消息-->
      		<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %c[%thread] : %msg%n</pattern>
      		<charset>utf-8</charset>
      	</encoder>
      	<!--输出路径-->
      	<file>C:/code/code1.log</file>
      	<rollingPolicy class="...">
      		<fileNamePattern>C:/code/code-%d{yyyy-MMdd}.log%i.gz</fileNamePattern>
      		<maxFileSize>1MB</maxFileSize>
      	</rollingPolicy>
      </appender>
      <!--
      <root> -> <appender-ref>不配置就不记录
      level用来设置打印级别,有TRACE,DEBUG,INFO,WARN,ERROR,ALL,OFF,default is debug
      -->
      <root level="ALL">
      	<appender-ref ref="CONSOLE" />
      	<appender-ref ref="FILE" />
      </root>
      

      日志级别设置

      日志级别有 TRACE<DEBUG<INFO<WARN<ERROR ,ALL,OFF,默认是debug,只输出级别高于设置级别的信息

· IO文件操作

File类可以定位和操作文件,但不能读写文件内容,I/O流技术可以读写文件

一些预备知识点:File类使用,方法递归,字符集,I/O流,字节流&&字符流

  1. File类

    代表了操作系统的文件对象(文件,文件夹)

    **主要功能:**定位文件,删除文件,获取文件信息,创建文件

    创建File对象:File f = new File("C:/file/b.xlsx")👉pathname支持 绝对路径相对路径 (相对工程目录)
    字节长度:f.length()
    获取绝对路径:f.getAbsolutePath()
    获取定义的路径:f.getPath()
    获取文件名称:f.getName()
    获取文件的最后修改时间:long time = f.lastModified()获得时间毫秒值
    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format()转换成人话
    判断是文件还是文件夹:.isFile() .isDirectory()

    创建新文件:f.createNewFile() //几乎不用,会自动创建
    创建一级目录:f.mkdir()
    创建多级目录:f.mkdirs()
    删除文件(夹):f.delete() //只能删除空文件夹

    获取当前目录下所有一级文件名称,返回到字符串数组:public String list()
    -----------------------------对象,返回文件对象数组:public File listFiles() //调用者不存在/为文件时返回null,空文件夹时返回空数组(包括隐藏文件)

  2. 方法递归Recursion

    复杂的问题 划分为 较小规模的问题

    基线情况 + 递归情况 + 递归公式向基线情况靠近

    • 递归经典问题

      在这里插入图片描述

          /**
      	x是第x天,f(x)是第x天摘的桃子
      	f(x) - f(x)/2 - 1 = f(x+1)
          */
          public static void main(String[] args) {
              System.out.println(Peaches(1));
          }
          private static int Peaches(int day){
              if(day == 10){
                  return 1;
              }
              else {
                  return 2*Peaches(day+1)+2;
              }
          }
      
    • 非规律递归–文件搜索

      遍历一级文件对象,判断是否是文件 → 如果是文件,是否是目标 → 如果是文件夹,返回步骤1

       public static void main(String[] args) {
              //X盘中找codeblocks.exe
              searchFile(new File("X:/codeblocks"),"codeblocks.exe");
          }
      
          /**
           * @param dir 被搜索的源目录
           * @param fileName  被搜索的文件名称
           */
          public static void searchFile(File dir,String fileName){
              if (dir!=null&&dir.isDirectory()){
                  //可以找
                  File[] files = dir.listFiles();
                  //防止拿了空文件夹
                  if (files!=null&&files.length>0){
                      for (File file : files) {
                          //进去之后判断当前是文件还是文件夹
                          if (file.isFile()){
                              if (file.getName().contains(fileName)){
                                  System.out.println("找到了"+file.getAbsolutePath());
                              }
                          }else{
                              searchFile(file,fileName);
                          }
                      }
                  }
              }else {
                  System.out.println(dir.getAbsolutePath()+"不是文件夹");
              }
          }
      
    • 非规律递归–啤酒问题
      //定义一个static成员变量用于存储可以买的酒数量
          public static int totalNum;
          public static int lastBottleNum;
          public static int lastCoverNum;
      
          public static void main(String[] args) {
              buy(10);
              System.out.println("总数:" + totalNum);
              System.out.println("剩余盖子:" + lastCoverNum);
              System.out.println("剩余瓶子:" + lastBottleNum);
          }
      
          private static void buy(int money) {
              int buyNum = money / 2; //第一次直接可以买的酒数量
              totalNum += buyNum;
              //盖子和瓶子换算成钱
              //统计本轮总的盖子数和瓶子数
              int coverNum = lastCoverNum + buyNum;   //盖子数 = 上一次的盖子数+传进来的money可以直接买的酒数
              int bottleNUm = lastBottleNum + buyNum; //类上
              //统计可以换算的钱
              int allMoney = 0;
              if (coverNum>=4){
                  allMoney += (coverNum/4)*2;
              }
              lastCoverNum = coverNum%4;
              if (bottleNUm>=2){
                  allMoney += (bottleNUm/2)*2;
              }
              lastBottleNum = bottleNUm%2;
      
              if (allMoney>=2){
                  buy(allMoney);
              }
          }
      
  3. 字符集

    ASCIIGBK(中国码表,兼容ASCII表,包含了几万个汉字,一个中文以两个字节存储),Unicode(万国码,容纳大多数国家的符号和文字,utf-8后中文以三个字节存储)
    在这里插入图片描述

    • 编码解码操作

      编码 – getBytes("GBK...")编码,返回字节数组

      解码 – String rs = new String(字节数组,"GBK...")编解码前后的字符集必须一致,否则乱码

  4. IO流

    磁盘与内存间的输入输出流,用以读写数据

    按传输的最小单位划分:字节流(音视频文件),字符流(纯文本文件)

    在这里插入图片描述

    1. 字节流

      /**
          一次读一个字节的FileInputStream(性能差,可能乱码)
      */
      //
      //创建一个文件字节输入流管道
      InputStream is = new FileInputStream(”path/File);
      //读取一个字节,返回
      int b1 = is.read();
      //读取下一个,返回(未读到返回-1)
      int b2 = is.read();
      //循环形式
      int b;
      while((b = is.read())!=-1){...}
      
      /**
          一次读一个字节数组的FileInputStream(可能乱码)
      */
      InputStream is = new FileInputStream(”path/File);
      //定义一个字节数组,用于读取()
      byte[] buffer = new byte[3];
      int len;
      while((len = is.read(buffer)) != -1){
      	sout.new String(buffer,0,len);
      }
      
      /**
          一次读完全部字节的FileInputStream(solve the messy code problem)
      */
      InputStream is = new FileInputStream(”path/File);
      //set buffer长度为文件字节长度(line6,7 is the same)
      byte[] buffer = new byte[(int)f.length()]
      byte[] buffer = is.readAllBytes();
      
      方法说明
      public void write(int a)写一个字节出去
      public void write(byte[] buffer)写一个字节数组
      public void write(byte[] buffer,int pos,int len)写一个字节数组的一部分出去
      flush()刷新流,可以继续写
      close()关闭流,释放资源(关闭前刷新,一旦关闭,无法写数据)
      /**
          FileOutputStream
      */
      OutputStream os = new FileOutputStream(”path/File,true);//true可以保证向文件追加数据而不是先清空再写
      byte[] buffer = "something".getBytes();
      os.write("/r/n".getBytes());//换行
      os.write(buffer);
      os.close(); 
      

      文件拷贝

      Step1. 根据数据源创建字节输入流对象
      Step2. 根据目的地创建字节输出流对象
      Step3. 读写数据,复制视频
      Step4. 释放资源

      InputStream is = new FileInputStream(”path/File);
      OutputStream os = new FileOutputStream(”path/File);
      byte[] buffer = new byte[1024]int len;
      while(len=is.read(buffer)!=-1){
      	os.write(buffer,0,len);
      os.close();
      is.close();
      

      在这里插入图片描述

      资源释放的方式

      try-catch-finally/try-with-resource

      因为finally语句一定会执行(除非JVM退出),因此适合做资源释放的工作.close()
      如果finally中有return的话,上面语句的return不会执行

      // JDK7更新的资源释放方式
      try(
      	//定义流对象,只能放置资源对象(implements了Closeable/AutoCloseable接口的对象),用完自动关闭
      	InputStream is = new FileInputStream(”path/File);
      	OutputStream os = new FileOutputStream(”path/File);
      ){...}
      catch{...}
      
    2. 字符流

      按单个字符读取,读取中文不会乱码(编码格式一致),更合适

      构造器:public FileReader(file/path)public FileWriter(file/path,true)

      methodstatement
      public int read()每次读取一个字符,读完返回-1
      public int read(char[] buffer)读取字符数组,返回字符个数,读完返回-1
      void write(…)可以写字符,字符数组(一部分),字符串(一部分)
      flush()刷新
      close()关闭流,释放
      /**
          一次读一个字节的FileReader(性能差)
      */
      Reader fr = new FileReader("path/file");
      while ((code=fr.read())!=-1){
      	sout.(char)code;
      }
      /**
          一次读一个字节数组的FileReader
      */
      char[] buffer = new char[1024];
      int len;
      while(len=fr.read(buffer)!=-1){
      	String rs = new String(buffer,0,len);
      	sout.rs;
      }
      
  5. 缓冲流

    自带缓冲区,提高原始流的性能

    在这里插入图片描述

    • 字节缓冲流

      BufferedInputStreamBufferedOutputStream(自带了8kb的缓冲池)

      InputStream bis = new BufferedInputStream(is)直接将FileInputStream包装成BufferedInputStream

    • 字符缓冲流

      BufferedReaderBufferedWriter

    代码与文件编码不一致的乱码问题

    • 字符输入转换流(InputStreamReader)

      InputStreamReader(InputStream in, String charsetName)

    • 字符输出转换流(OutputStreamReader)=“String”.getBytes()

      控制写出的字符使用的编码

      OutputStreamWriter(OutputStream out, Charset cs)

  6. 序列化对象

    • 对象序列化

      定义:以内存为基准,将内存的对象存储到磁盘文件中

      使用对象字节输出流ObjectOutputStream

      构造器statement
      public ObjectOutputStream(OutputStream out)低级字节输出流包装成高级的对象字节输出流

      对象class必须implements Serializable才能序列化

    • 对象反序列化

      定义:以内存为基准,将磁盘文件中的对象数据恢复成内存中的对象

      使用对象字节输入流ObjectInputStream

      构造器statement
      public ObjectInputStream(InputStream in)低级字节输入流包装成高级的对象字节输入流

      private transient password敏感信息可以用transient修饰,不参与序列化

      可以定义final字段来规定序列化的版本号

  7. 打印流

    两种:FileOutputStream -> PrintStream 或者 FileWriter -> PrintWriter

    最强大的写数据到文件流,可以实现所见即所得

    构造器statement
    public PrintStream(OutputStream os)直接通向字节输出流管道
    public PrintStream(File f)直接通向文件对象
    public PrintStream(String filepath)直接通向文件路径
    methodstatement
    public print(xxx xx)打印任意类型数据

    打印功能上PrintStream与PrintWriter无异(归属有异,写数据上PrintStream 只能写字节数据)

  8. Properties

    Map -> HashTable -> Properties

    其实就是一个Map集合,但不当集合用(因为HashMap更好用)

    核心作用:Properties代表一个属性文件,可以把自己对象中的键值对信息存入一个属性文件中

    属性文件:.properties结尾的文件,里面的内容都是key=value,后续做系统配置信息

    method:properties.store()properties.getProperties()properties.setProperties()

  9. Commons-IO

    apache基金会提供的有关IO操作的类库,主要的类有FileUtils,IOUtils

  • 线程

多线程的创建、Thread类的常用方法、线程安全、线程同步、线程通信、线程池、定时器、线程状态…

  1. 创建Thread

    1. 方案一(继承Thread类)

      ① 定义一个myThread子类继承java.lang.Thread,重写run()

      ② 创建myThread类的对象

      ③ 调用线程对象的start()方法启动线程

      缺点:继承Thread类,无法继承其他类,不利于扩展

      //多线程创建方式一:继承Thread类
      public class createThread {
          public static void main(String[] args) {
              Thread thread = new myThread();
              thread.start();
              task;
          }
      }
      class myThread extends Thread{
      //    重写run()
      
          @Override
          public void run() {
              task;
          }
      }
      
    2. 方案二(实现Runnable接口)

      ① 定义一个线程任务类MyRunnable实现Runnable接口,重写run()

      ② 创建MyRunnable任务对象

      ③ 把MyRunnable的任务对象交给Thread处理

      ④ 调用线程对象的start()方法启动线程

      缺点:多了一层对象包装,线程的执行结果无法直接return(第一种也无法直接return)

      // 实现Runnable接口,创建线程
      public class createThread2 {
          public static void main(String[] args) {
              //创建任务对象
              Runnable target = new MyRunnable();
              //把任务对象交给Thread处理
              Thread t = new Thread(target);
              //启动线程
              t.start();
              task;
          }
      }
      class MyRunnable implements Runnable{
          @Override
          public void run() {
              task;
          }
      }
      

      另一种写法(匿名内部类)

      public class createThreadOther {
          public static void main(String[] args) {
              Runnable target = new Runnable() {
                  @Override
                  public void run() {
                      for (int i = 0; i < 10; i++) {
                          System.out.println("子线程执行输出:"+i);
                      }
                  }
              };
              Thread t = new Thread(target);
              t.start();
              for (int i = 0; i < 10; i++) {
                  System.out.println("主线程执行输出:"+i);
              }
          }
      }
      
      //lambda表达式简化
      public class createThreadOther {
          public static void main(String[] args) {
                new Thread(() -> {
      	    for(int i=0;i<10;i++){
      	      System.out.println("子线程执行输出:"+i);
      	    }
      	  }).start();
              for (int i = 0; i < 10; i++) {
                  System.out.println("主线程执行输出:"+i);
              }
          }
      }
      
    3. 方案三(JDK5.0 Callable&FutureTask,解决不能直接return线程执行结果的问题)

      ① 得到任务对象

      • 定义类实现Callable接口,重写call(),封装要做的事
      • 用FutureTask把Callable对象封装成线程任务对象

      ② 线程任务对象交给Thread处理

      ③ 调用Thread的start()启动线程,执行任务

      ④ 线程执行完毕后,用FutureTask的get()获取任务执行的结果

      缺点:编码复杂

      优点:扩展性强,实现接口的同时可继承其他类;可以得到线程执行的结果

      import java.util.concurrent.Callable;
      import java.util.concurrent.ExecutionException;
      import java.util.concurrent.FutureTask;
      // Callable+FutureTask
      public class createThread3 {
          public static void main(String[] args) {
              // 3.创建Callable任务对象
              Callable<String> call = new MyCallable(100);
              // 4.把Callable对象封装成FutureTask对象(FutureTask是Runable的对象)
              FutureTask<String> f1 = new FutureTask<>(call);
              // 5.交给线程
              Thread t1 = new Thread(f1);
              t1.start();
              try {
                  String rs1 = f1.get();
                  System.out.println("第一个结果是:"+rs1);
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
      // 1.定义任务类 实现Callable接口
      class MyCallable implements Callable<String>{
          private int n;
          public MyCallable(int n) {
              this.n = n;
          }
          // 2.重写call(),线程的任务方法
          @Override
          public String call() throws Exception {
              int sum = 0;
              for (int i = 0; i <= n; i++) {
                  sum+=i;
              }
              return "子线程返回的结果是:"+sum;
          }
      }
      
  2. Thread的常用方法

    • 如何区分不同线程

      methodstatement
      getName()获取当前线程名
      setName(String name)修改线程名
      currentThread()获取当前线程对象
      package com.xyf.thread.APIofThread;
      
      public class distinguishThread {
          public static void main(String[] args) {
              Thread t1 = new MyTHread();
              t1.setName("no1");
              t1.start();
              System.out.println(t1.getName());
              Thread t2 = new MyTHread();
              t2.setName("no2");
              t2.start();
              System.out.println(t2.getName());
              //to get main thread
              Thread m = Thread.currentThread();
              System.out.println(m.getName());
              for (int i = 0; i < 5; i++) {
                  System.out.println("main线程输出:"+i);
              }
          }
      }
      class MyTHread extends Thread{
          @Override
          public void run() {
              for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()+"子线程输出:"+i);
              }
          }
      }
      

      也可以在MyThread类中修改构造器

      Thread的构造器

      constructorstatement
      public Thread(Stirng name)为当前线程指定名称
      public Thread(Runable target)封装Runnable对象成为线程对象
      public Thread(Runable target,Stirng name)封装Runnable对象成为,并指定名称
      public MyThread(String name){
          //调用父类Thread的构造器
          super(name);
      }
      
    • 线程的休眠方法

      methodstatement
      public static void sleep(long time)线程休眠time时间再执行,单位ms
  3. 线程安全

    多个Thread同时操作同一个共享资源时可能会出现的业务安全问题

    class Account{
        private String cardID;
        private double money;
    
        public Account(String cardID, double money) {
            this.cardID = cardID;
            this.money = money;
        }
        public Account() {
        }
        public String getCardID() {
            return cardID;
        }
        public void setCardID(String cardID) {
            this.cardID = cardID;
        }
        public double getMoney() {
            return money;
        }
        public void setMoney(double money) {
            this.money = money;
        }
        public void drawMoney(double money) {
            //1.判断谁来取钱
            String name = Thread.currentThread().getName();
            //2.判断账户钱够不够
            if (this.money>=money){
                //3.取钱,更新余额
                System.out.println(name+"取钱成功,取出:"+money+"元");
                this.money-=money;
                System.out.println(name+"取钱后剩余:"+this.money);
            }else {
                System.out.println(name+"取钱失败,余额不足");
            }
        }
    }
    public class DrawThread extends Thread{
        //取钱线程类
        private Account account;
        public DrawThread(Account account,String name){
            super(name);
            this.account=account;
        }
        @Override
        public void run() {
            account.drawMoney(100000);
        }
    }
    public class run{
        public static void main(String[] args) {
            //账户实例(用户共享账户)
            Account a = new Account("123456",100000);
            //创建两个用户线程
            new DrawThread(a,"Tom").start();
            new DrawThread(a,"Ivan").start();
        }
    }
    
  4. 线程同步

    核心思想

    • lock

      将共享资源上锁,一次只能一个Thread进入,访问完毕后解锁,Other thread才能进

    1. 上锁方法一:同步代码块

      Function:把出现线程安全的code上锁

      Principal:每次只能一个Thread进入,执行完毕后自动解锁,其他Thread才能执行

      synchronized(同步锁对象){
      	//   ↑使用共享资源作为锁对象(实例方法用this,静态方法用类名.class)
      	//操作共享资源的代码
      	...
      }
      
    2. 上锁方法二:同步方法

      Function:把出现线程安全的function上锁

      Principal:same

      修饰符 synchronized 返回值类型 方法名称(para1,para2,...){
      	//操作共享资源的代码
      	...
      }
      

      底层原理:其实方法前加synchronized与同步代码块方法是一样的,底层有隐式锁对象锁住整个方法代码,实例-this,静态-类名.class

    3. 上锁方法三:Lock锁

      JDK5后提供了Lock锁对象,Lock是接口不能实例化,用其实现类public ReentrantLock() 构建Lock锁对象

      lock()/unlock()方法上/解锁,最好放在try-finally结构中保证始终解锁

  5. 线程通信

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值