JavaSE基础

 Java入门

1、IDEA常用快捷键

2、编译、运行代码

3、JDK组成、跨平台原理

4、关键字

 

Java语法

1、基本数据类型

2、类型转换

2.1、自动类型转换

2.2、表达式的自动类型转换

 2.3、强制类型转换

常用API

 1、String

1.1、 String对象的创建

 1.2、String对象常用API 

 1.3、String的注意事项

2、时间API

2.1、传统API:Date

import java.util.Date;

public class DataDemo {
    public static void main(String[] args) {
        //1、系统时间信息
        Date date = new Date();
        System.out.printf(date.toString());

        //2、获取时间毫秒值
        long time = date.getTime();
        System.out.println(time);

        //3、时间戳转日期(再加2s)
        time += 2*1000;
        Date date1 = new Date(time);
        System.out.println(date1);

        //4、日期转时间戳
        Date date2 = new Date();
        date2.setTime(time);
        System.out.println(date2);
    }
}

 2.2、SimpleDateFormat 时间格式

 

public class SimpleDateFormatDemo {
    public static void main(String[] args) throws ParseException {

        Date date = new Date();
        System.out.println(date);

        long time = date.getTime();
        System.out.println(time);

        //格式化日期对象、时间、毫秒值
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss EEE a");
        String format1 = sdf.format(date);
        System.out.println(format1);

        String format2 = sdf.format(time );
        System.out.println(format2);


        //SimpleDateFormat解析字符串时间成为日期对象。
        String dateStr = "2022-12-12 12:12:11";
        //1、创建简单日期格式化对象,指定的时间格式必须与被解析的时间格式一模一样,否则程序会出bug.
        SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d2 = sdf2.parse(dateStr);
        System.out.println(d2);

    }
}

 2.3、JDK8新增时间

时间戳 

格式化器 

 集合框架

1、集合体系结构

2、Collection集合

2.1、常用方法:

2.2、迭代器:

/**
 * 迭代器
 */
public class CollectionTest01 {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<>();c.add("赵敏");
        c.add("小昭");
        c.add("素素");
        c.add("灭绝");
        System.out.println(c);

        // 使用迭代器遍历集合
        Iterator<String> it = c.iterator();
        // it.hasNext()即迭代器是否有下一个元素
        while (it.hasNext()) {
            // it.next()即迭代器获取下一个元素,初始是第一个元素
            String s = it.next();
            System.out.println(s);
        }
    }
}

2.3、增强for循环

2.4、Lambda表达式遍历集合

3、List集合

3.1、List集合特有方法

3.2、ArrayList集合底层原来

3.3、LinkedList集合底层原理

LinkedList是基于双链表实现的

比如银行排队问题,就是典型的队列问题

4、Set集合

4.1、特点

4.2、HashSet集合底层原理

如果数组快占满了,就会进行2倍扩容,即当数据中的元素达到16*0.75=12时就会扩容

如果链表过长,导致查询性能下降,怎么办呢?在JDK8之后引入红黑树解决

有个问题:那么如果对于内容相同的不同对象,比如两个信息一样的学生对象,按理应该去重,但是在HashSet集合中是使用equals()方法来比较的,即比较地址,那么就会两个对象都存入集合中,如果想要内容相同的对象也进行去重,就要重写equals()方法:Alt+Ins选择重写equals和hashCode,那么IDEA就会自动生成重写后的equals和hashCode方法

4.3、树

那么问题来了,什么是红黑树呢?

红黑树底层有自己的自平衡的算法

4.4、LinkedHashSet集合底层原理

和HashSet底层一样,只是引入了双链表机制,即每一个元素都记录了上一个和下一个元素的地址,从而实现元素有序,但是这样会更耗费内存

4.5、TreeSet集合

红黑树的左大右小的数据结构,从左往右遍历本身就可以实现升序排序

/**
 * 方式一
 */
@Data
public class Student implements Comparable<Student> {
    private String name;
    private Integer age;

    // 自定义比较规则
    @Override
    public int compareTo(Student o) {
        //如果认为左边对象大于右边对象返回正整数
        //如果认为左边对象小于右边对象返回负整数
        // 如果认为左边对象等于右边对象返回0
        // 需求:按照年龄升序排序
        return this.age - o.age;
    }
}
public class SetTest {
    public static void main(String[] args) {
        // 方式二
        Set<Student> s = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //如果认为左边对象大于右边对象返回正整数
                //如果认为左边对象小于右边对象返回负整数
                // 如果认为左边对象等于右边对象返回0
                // 需求:按照年龄升序排序
                return o1.getAge() - o2.getAge();
            }
        });
        s.add(new Student("张三",10));
        s.add(new Student("李四",30));
        s.add(new Student("王五",20));
        System.out.println(s);
    }
}

5、集合的并发修改异常问题

因为当 list.remove() 删除元素之后,元素的个数就会减少,往后的元素就会向左移动,对于的下标也会减一

6、Map集合

6.1、Map集合公有

  

6.2、HashMap集合底层原理

HashMap将一个键值对当成一个Entry对象,其他的和HashSet集合一样的原理

6.3、LinkedHashMap集合底层原理

6.4、TreeMap集合底层原理

 Java8 新特性

1、接口上

新增了默认方法default、私有方法private、静态方法static

public class InterfaceTest {
    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        myClass.defaultMethod();
        MyInterface.staticMethod();
    }
    
    public interface MyInterface {
        default void defaultMethod() {
            System.out.println("接口中的默认方法,意味着实现类不用重写也能调用");
            privateMethod();
        }
        
        private void privateMethod() {
            System.out.println("这接口这的私有方法,JDK9之后才支持");
        }
        
        static void staticMethod(){
            System.out.println("接口中的静态方法");
        }
    }
    
    public static class MyClass implements MyInterface {}
}

2、Lambda表达式

2.1、Lambda之常规写法(只能对接口简化)

public class LambdaTest01 {
    public static void main(String[] args) {
        // 原先写法(创建这个接口的匿名内部类)
        A a = new A() {
            @Override
            public void test1() {
                System.out.println("原先写法");
                System.out.println("test1");
            }
        };
        a.test1();

        // Lambda写法
        A a1 = () -> {
            System.out.println("原先写法");
            System.out.println("test1");
        };
        a1.test1();
    }
}

interface A{
    void test1();
}

2.2 、Lambda之静态方法的引用

public class LambdaTest02 {
    public static void main(String[] args) {
        Student[] student = new Student[3];
        student[0] = new Student(18, "张三");
        student[1] = new Student(19, "李四");
        student[2] = new Student(20, "王五");

        // 原先写法 sort中第一个参数是要排序的数组,第二个参数是定义排序规则的比较器(Comparator)
        Arrays.sort(student, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //这个方法返回一个整数,该整数用于表示o1和o2的顺序。
                // 如果返回值小于0,则o1排在o2之前;如果返回值等于0,
                // 则o1和o2的顺序无关紧要;如果返回值大于0,则o1排在o2之后
                return o1.getAge() - o2.getAge();  // 按照年龄排序()
            }
        });

        // 现在使用Lambda表达式
        Arrays.sort(student, (o1, o2) -> o1.getAge() - o2.getAge());
        
        // 静态方法的引用,进一步简化Lambda表达式 ******************************
        A a = new A();
        Arrays.sort(student, a::myCompare);
        System.out.println(Arrays.toString(student));
    }

    static class A{
        public int myCompare(Student o1, Student o2) {
            return o1.getAge() - o2.getAge();  // 按照年龄排序()
        }
    }

    @Data   // 自动生成getters和setters和toString方法
    @AllArgsConstructor // 自动生成全参构造方法
    public static class Student{
        private int age;
        private String name;
    }
}

2.3、Lambda之特定方法的引用

public class LambdaTest03 {
    public static void main(String[] args) {
        B b1 = new B();
        // 常规Lambda
        A a1 = (B b, Integer s) -> b.method(s);
        // 特定方法的引用的Lambda
        A a2 = B::method;

        a1.test(b1, 1);
        a2.test(b1, 3);
    }

    interface A{
        void test(B b, Integer i);
    }

    static class B{
        void method(Integer i){
            System.out.println(i);
        }
    }
}

2.4、Lambda之构造器引用

public class LambdaTest04 {
    public static void main(String[] args) {
         
        A a1 = new A() {
            @Override
            public void testB(String name, Integer age) {
                new B(name, age);
            }
        };

        // 常规Lambda写法
        A a2 = (name, age) -> new B(name, age);
        a2.testB("小红", 18);
        // Lambda写法之构造器引用
        A a3 = B::new;
        a3.testB("小张", 18);

    }

    interface A{
        void testB(String name, Integer age);
    }

    @Data // getter、setter、toString方法
    @AllArgsConstructor //全参构造函数
   static class B{
        private String name;
        private Integer age;
   }
}

3、Stream流

3.1入门案例

public class StreamTest01 {
    // 入门案例
    public static void main(String[] args) {
        // 取出名字以张开头的并且3个字的名字
        List<String> names = new ArrayList<>();
        // 为names集合添加多个元素
        Collections.addAll(names,"张三丰", "张无忌", "周芷若", "赵敏", "张强");
        System.out.println(names);
        // names =[张三丰,张无忌,周芷若,赵敏,张强]
        // 找出姓张,且是3个字的名字,存入到一个新集合中去
        List<String> list = new ArrayList<>();
        for (String name : names) {
            //startsWith是字符串的一个方法
            if (name.startsWith("张") && name.length() == 3) {
                list.add(name);
            }
        }
        System.out.println(list);


        // 使用Stream流来处理(lambda 表达式)
        List<String> list2 = names.stream().filter(s -> s.startsWith("张"))
                .filter(a -> a.length()==3).collect(Collectors.toList());
        System.out.println(list2);
    }
}

3.2、获取Stream流

/**
 * 获取Stream流
 */
public class GetStream {
    public static void main(String[] args) {
        // 1、获取List集合的Stream流
        List<String> names = new ArrayList<>();
        Collections.addAll(names,"张三丰", "张无忌", "周芷若", "赵敏", "张强");
        // 2、获取List集合的Stream流
        Stream<String> stream = names.stream();


        // 2、获取Set集合的Stream流
        Set<String> set = new HashSet<>();
        Collections.addAll(set, "刘德华", "张曼玉", "蜘蛛精", "马德","德玛西亚");
        Stream<String> stream1 = set.stream();
        // 获取Set集合Stream流
        // filter过滤方法,s是集合遍历的元素,contains是字符串中的方法,表示字符串中含有“德”
        // forEach方法,s是过滤之后的集合遍历的元素,表示打印集合中的元素
        stream1.filter(s -> s.contains("德")).forEach(s -> System.out .println(s));


        // 3、获取Map集合的Stream流
        Map<String,Double> map = new HashMap<>();
        map.put("古力娜扎", 172.3);
        map.put("迪丽热巴", 168.3);
        map.put("马尔扎哈", 166.3);
        // 将map集合中的key取出来存入Set集合
        Set<String> keys = map.keySet();
        // 获取stream流
        Stream<String> ks = keys.stream();
        // 将map集合中的key取出来存入Set集合
        Collection<Double> values = map.values();
        // 获取stream流方式一
        Stream<Double> vs = values.stream();
        // 也可以用Map.Entry<>方法()存入Set集合
        // entries打印的值为:[迪丽热巴=168.3, 古力娜扎=172.3, 马尔扎哈=166.3]
        Set<Map.Entry<String, Double>> entries = map.entrySet() ;
        // 获取stream流方式二
        Stream<Map.Entry<String, Double>> kvs = entries.stream();
        // e表示Set集合中遍历的元素,e.getKey().contains("扎")判断元素的key是否包含“扎”
        kvs.filter(e -> e.getKey().contains("扎"))
                //forEach表示遍历过滤后的集合,e.getValue()获取元素的value
                .forEach(e -> System.out.println(e.getKey()+ "-->" + e.getValue()));


        // 4、如何获取数组的Stream流?
        String[] names2 = {"张翠山", "东方不败", "唐大山", "独孤求败"};
        // 获取stream流方式一
        Stream<String> s1 = Arrays.stream(names2);
        // 获取stream流方式二
        Stream<String> s2 = Stream.of(names2);
    }
}

3.3、常见的Stream流中间方法与终结方法

多线程

1、概述

2、多线程的创建

2.1、extends Thread 创建

/**
 * extends Thread 线程的创建
 */
public class ThreadTest01 {
    // main是一条主线程
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        // 启动线程,开启另一个线程
        myThread.start();
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程输出:" + i);
        }
    }
    // 方式一创建一个子线程
    public  static class MyThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                System.out.println("子线程输出:" + i);
            }
        }
    }
}

2.2、implements Runnable 创建

/**
 * implements Runnable 线程的创建
 */
public class ThreadTest02 {
    // main是一条主线程
    public static void main(String[] args) {
        // 创建任务对象
        Runnable thread = new MyThread();
        // 把任务对象交给线程对象去执行
        new Thread(thread).start();

        for (int i = 0; i < 5; i++) {
            System.out.println("主线程输出:" + i);
        }
    }
    // 方式二创建一个子线程
    public  static class MyThread implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                System.out.println("子线程输出:" + i);
            }
        }
    }
}

最后还可以使用Lambda表达式继续简化一下代码

2.3、implements Callable 创建

/**
 * implements Callable 线程的创建
 */
public class ThreadTest03 {
    // main是一条主线程
    public static void main(String[] args) throws Exception{
        // 创建Callable对象
        Callable<String> callable = new MyThread(100);
        // 把Callable对象封装成FutureTask对象(未来任务对象)
        // 其实FutureTask也实现了Runnable接口
        FutureTask<String> futureTask = new FutureTask<>(callable);
        new Thread(futureTask).start();

        //获取线程执行完毕后返回的结果
        //注意:如果执行到这儿,假如上面的线程还没有执行完毕
        //这里的代码会暂停,等待上面线程执行完毕后才会获取结果
        String s = futureTask.get();
        System.out.println(s);
    }
    // 方式三创建一个子线程,Callable<String>中的String是call方法的返回值
    public  static class MyThread implements Callable<String> {
        private int n;
        public MyThread(int n) {
            this.n = n;
        }
        @Override
        public String call() {
            int sum = 0;
            for (int i = 1; i < n; i++) {
                sum = sum + i;
            }
            return "线程求出了:1+2+3+...+"+n+"=" + sum;
        }
    }
}

3、线程安全问题

/**
 * 线程安全案例
 */
public class ThreadSecurityTest {
    public static void main(String[] args) {
        // 1.创建一个账户对象
        Account account = new Account("ICBC-110", 10000);
        // 2.创建两个线程,去同一个账户取钱
        new DrawThread(account, 10000, "小红").start();
        new DrawThread(account, 10000, "小明").start();
    }

    // 账户类
    @Data
    @AllArgsConstructor
    public static class Account {
        private String cardId;  // 卡号
        private double money;  // 余额

        // 取钱逻辑
        public void drawMoney(double getMoney) {
            // 获取当前线程名字
            String name = Thread.currentThread().getName();
            if (money >= getMoney) {
                System.out.println(name + "取钱" +getMoney + "元成功");
                money -= getMoney;
                System.out.println("余额为:" + money);
            } else {
                System.out.println(name + "取钱:余额不足,余额为:" + money);
            }
        }
    }

    // 线程取钱类
    @Data
    public static class DrawThread extends Thread {
        private Account account;
        private double money;   // 取钱金额
        public DrawThread(Account account, double money, String name) {
            // 设置线程名称
            super(name);
            this.account = account;
            this.money = money;
        }
        @Override
        public void run() {
            // 调用账户取钱方法
            account.drawMoney(money);
        }
    }
}

4、解决线程安全---线程同步

4.1、同步代码块

使用synchronized关键字加锁

            // 使用同步锁"@@小飞"相当于锁的一个唯一标识
            synchronized ("@@小飞") {
                if (money >= getMoney) {
                    System.out.println(name + "取钱" +getMoney + "元成功");
                    money -= getMoney;
                    System.out.println("余额为:" + money);
                } else {
                    System.out.println(name + "取钱:余额不足,余额为:" + money);
                }
            }

4.2、同步方法

锁范围小意味着性能好

4.3、Lock锁

加锁代码使用try-catch-finally代码块,防止程序异常导致无法解锁,创建Lock锁使用final关键字,防止被修改

4.4、乐观锁与悲观锁

无论是使用synchronized关键字还是使用Lock锁其实本质上都是基于悲观锁的。都是先上锁,线程无法同时执行。如果想线程安全又要同时执行,就可以使用乐观锁。
悲观锁:一上来就加锁,没有安全感。每次只能一个线程进入访问完毕后,再解锁。线程安全,性能较差!
乐观锁:一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问题的时候才开始控制。要修改数据的时候再去比较原理的数据是否是之前拿到的,如果是就修改,不是就把最高操作作废,重写拿数据修改。 线程安全,性能较好

线程使用乐观锁:Java中提供了乐观锁的原子类

5、线程通信(了解)

/**
 * 线程通信,了解
 */
public class 线程通信 {
    public static void main(String[] args) {
        // 创建2个厨师线程(生产者)
        Desk desk = new Desk();
//========================================================
        new Thread(() -> {
            while (true){
                desk.put();
            }
        },"厨师1").start();
//========================================================
        new Thread(() -> {
            while (true){
                desk.put();
            }
        },"厨师2").start();
//========================================================
        // 创建2个吃货线程(消费者)
        new Thread(() -> {
            while (true){
                desk.get();
            }
        }, "吃货1").start();
//========================================================
        new Thread(() -> {
            while (true){
                desk.get();
            }
        }, "吃货2").start();
    }


    public static class Desk{
        private List<String> list = new ArrayList<>();
        //厨师做包子
        public synchronized void put() {
            try {
                if (list.size() == 0){
                    String name = Thread.currentThread().getName();
                    list.add(name + "做的包子");
                    System.out.println(name + "做好了一个包子");
                    // 睡眠1秒
                    Thread.sleep(1000);

                    // 唤醒其他线程
                    this.notifyAll();
                    // 等待自己(等待自己的代码要写在最后面)
                    this.wait();
                }else {
                    // 唤醒其他线程
                    this.notifyAll();
                    // 等待自己(等待自己的代码要写在最后面)
                    this.wait();
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        //吃包子
        public synchronized void get() {
            try {
                String name = Thread.currentThread().getName();
                if (list.size() ==1){
                    System.out.println(name + "吃了" + list.get(0));
                    list.clear();

                    // 睡眠1秒
                    Thread.sleep(1000);
                    // 唤醒其他线程
                    this.notifyAll();
                    // 等待自己(等待自己的代码要写在最后面)
                    this.wait();
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

6、线程池

6.1、认识线程池

当线程池中的线程忙完当前的任务就会立马去处理其他任务,线程是复用的,这样就不会因为创建大量线程导致cpu飙升

6.2、创建线程池

public class ThreadPoolTest01 {
    //    public ThreadPoolExecutor(int corePoolSize,
    //                              int maximumPoolSize,
    //                              long keepAliveTime,
    //                              TimeUnit unit,
    //                              BlockingQueue<Runnable> workQueue,
    //                              ThreadFactory threadFactory,
    //                              RejectedExecutionHandler handler) {
    public static void main(String[] args) {
        // ThreadPoolExecutor这个类是线程池的实现类
        // 但是他有很多参数,可以按住Ctrl点击ThreadPoolExecutor查看源码中的参数
        new ThreadPoolExecutor(3,5,10,
                // 4是任务队列的数量
                TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
    }
}

按住Ctrl键点击ThreadPoolExecutor就可以查看源码中有哪些参数了

6.3、线程池处理Runnable任务

    public static void main(String[] args) {
        // ThreadPoolExecutor这个类是线程池的实现类
        // 但是他有很多参数,可以按住Ctrl点击ThreadPoolExecutor查看源码中的参数
        ExecutorService pool = new ThreadPoolExecutor(3,5,10,
                // 4是任务队列的数量
                TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        Runnable target = new MyRunnable();
        pool.execute(target);//线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
        pool.execute(target);//线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
        pool.execute(target);//线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
        pool.execute(target);//线程复用
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);//后面忙不过来就会创建;临时线程
        pool.execute(target);
    }
    public static class MyRunnable implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "===>666");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

 6.4、Executors工具类实现线程池

6.5、多线程并发、并行、生命周期

 网路编程

1、概述

2、网路通信三要素

2.1、IP

public class InetAddressTest {
    public static void main(String[] args) throws Exception {
        // 创建获取本机IP的对象
        InetAddress ip1 = InetAddress.getLocalHost();
        System.out.println(ip1.getHostName());
        System.out.println(ip1.getHostAddress());

        // 获取指定IP或者域名的IP地址对象。
        InetAddress ip2 = InetAddress.getByName ("www.baidu.com");
        System.out.println(ip2.getHostName());
        System.out.println(ip2.getHostAddress() );

        // 判断指定百度IP地址是否可达。就是ping命令
        System.out.println(ip2.isReachable(3000));
    }
}

2.2、端口号

2.3、协议

3、UDP通信

3.1、快速入门

/**
 * 发送数据的客户端
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建客户端对象
        DatagramSocket socket = new DatagramSocket();

        //2、创建数据包对象封装要发出去的数据
        /*参数一:封装要发出去的数据。
           参数二:发送出去的数据大小(字节个数)参数三:。
         */
        byte[] bytes ="我是快乐的客户端,我爱你abc".getBytes();
        // bytes是封装要发出去的数据,bytes.length发送出去的数据大小(字节个数)
        // InetAddress.getLocalHost(),6666服务端的IP地址(找到服务端主机>参数四:服务端程序的端口
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(),6666);

        //3、开始正式发送这个数据包的数据出去了
        socket.send(packet);
        System.out.println("客户端数据发送完毕~~~" );
        socket.close(); //释放资源!
    }
}


/**
 * 服务端的类
 */
public class Server {
    public static void main(String[] args) throws Exception {
        // 1、创建一个服务端对象和注册端口
        DatagramSocket socket = new DatagramSocket(6666);
        // 2、创建一个接收数据包对象的容器大小
        byte[] buffer = new byte[ 1024* 64]; // 64KB.
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
        //3、开始正式使用数据包来接收客户端发来的数据
        socket.receive(packet);
        //4、从字节数组中,把接收到的数据直接打印出来
        //接收多少就倒出多少
        //获取本次数据包接收了多少数据。
        int len = packet.getLength();
        String rs = new String(buffer, 0, len);
        System.out.println(rs);

        //5、获取客户端的IP地址和端口号
        InetAddress ip = packet.getAddress();
        int port = packet.getPort();
        System.out.println("客户端的IP地址:" + ip + ",客户端的端口号为:" + port);

        socket.close();
    }
}

先启动sever类,然后再启动client类就可以在sever类的控制台中看到接收的消息

3.2、多端多发

可以多个客户端发送消息

/**
 * 发送数据的客户端
 * 多端多发
 */
public class Client02 {
    public static void main(String[] args) throws Exception {
        // 1、创建客户端对象
        DatagramSocket socket = new DatagramSocket();

        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println("请输入发送的消息...");
            String msg = scanner.nextLine();
            if (msg.equals("exit")){
                System.out.println("客户端已退出~~~" );
                socket.close(); //释放资源!
                break;
            }
            byte[] bytes = msg.getBytes();
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(),6666);
            socket.send(packet);
        }
    }
}


/**
 * 服务端的类
 * 多端多发
 */
public class Server02 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个服务端对象和注册端口
        DatagramSocket socket = new DatagramSocket(6666);
        // 2、创建一个接收数据包对象的容器大小
        byte[] buffer = new byte[1024* 64]; // 64KB.
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

        while (true) {
            //3、开始正式使用数据包来接收客户端发来的数据
            socket.receive(packet);
            //4、从字节数组中,把接收到的数据直接打印出来
            //接收多少就倒出多少
            //获取本次数据包接收了多少数据。
            int len = packet.getLength();
            String rs = new String(buffer, 0, len);
            System.err.println(rs);

            //5、获取客户端的IP地址和端口号
            InetAddress ip = packet.getAddress();
            int port = packet.getPort();
            System.out.println("客户端的IP地址:" + ip + ",客户端的端口号为:" + port);
        }
    }
}

4、TCP通信

4.1、快速入门

public class Client01 {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("127.0.0.1", 8888);
        // 获取Socket的输出流
        OutputStream os = socket.getOutputStream();
        // 将低级流包装成一个数据流
        DataOutputStream dos = new DataOutputStream(os);

        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println("请输入发送的消息...");
            String msg = scanner.nextLine();
            if (msg.equals("exit")) {
                System.out.println("客户端已退出~~~");
                dos.close();
                socket.close(); //释放资源!
                break;
            }
            // 写入数据
            dos.writeUTF(msg);
        }
    }
}


public class Server01 {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(8888);
        // 阻塞等待客户端连接
        Socket socket = serverSocket.accept();
        InputStream inputStream = socket.getInputStream();
        DataInputStream dis = new DataInputStream(inputStream);

        while (true) {
            try {
                String s = dis.readUTF();
                System.out.println(s);
            } catch (Exception e) {
                System.out.println("客户端已经离线了~~~");
                dis.close();
                socket.close();
                break;
            }
        }
    }
}

4.2、多客户端连接

/**
 * 服务端接收多端消息
 */
public class Server02 {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(8888);
        while (true){
            // 阻塞等待客户端连接
            Socket socket = serverSocket.accept();
            System.out.println("有人上线了:"+ socket.getRemoteSocketAddress());
            new Thread(new ServerThread(socket)).start();
        }
    }

    public static class ServerThread implements Runnable{
        private Socket socket;
        public ServerThread(Socket socket) {
            this.socket = socket;
        }
        @Override
        public void run() {
                try {
                    InputStream inputStream = socket.getInputStream();
                    DataInputStream dis = new DataInputStream(inputStream);
                    while (true) {
                        try {
                            // 当读取数据的管道异常,说明客户端下线
                            String s = dis.readUTF();
                            System.out.println(socket.getRemoteSocketAddress() + ":" +s);
                        } catch (Exception e) {
                            System.out.println("有人下线了:"+ socket.getRemoteSocketAddress());
                            dis.close();
                            socket.close();
                            break;
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
        }
    }
}

4.3、群聊

**
 * 群聊客户端
 */
public class Client {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("127.0.0.1", 8888);
        // 获取Socket的输出流
        OutputStream os = socket.getOutputStream();
        // 将低级流包装成一个数据流
        DataOutputStream dos = new DataOutputStream(os);

        new Thread(new ClientReader(socket)).start();

        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println("请输入发送的消息...");
            String msg = scanner.nextLine();
            if (msg.equals("exit")) {
                System.out.println("客户端已退出~~~");
                dos.close();
                socket.close(); //释放资源!
                break;
            }
            // 写入数据
            dos.writeUTF(msg);
        }
    }


    public static class ClientReader implements Runnable{
        private Socket socket;
        public ClientReader(Socket socket) {
            this.socket = socket;
        }
        @Override
        public void run() {
            while (true) {
                try {
                    InputStream inputStream = socket.getInputStream();
                    DataInputStream dis = new DataInputStream(inputStream);
                    String s = null;
                    try {
                        s = dis.readUTF();
                        System.out.println(s);
                    } catch (Exception e) {
                        System.out.println("服务器出问题了");
                        dis.close();
                        socket.close();
                        break;
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}


/**
 * 群聊服务端
 */
public class Server {
    private static List<Socket> socketList = new ArrayList<>();

    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(8888);
        while (true){
            // 阻塞等待客户端连接
            Socket socket = serverSocket.accept();
            socketList.add(socket);
            System.out.println("有人上线了:"+ socket.getRemoteSocketAddress());
            new Thread(new ServerThread(socket)).start();
        }
    }

    public static class ServerThread implements Runnable{
        private Socket socket;
        public ServerThread(Socket socket) {
            this.socket = socket;
        }
        @Override
        public void run() {
                try {
                    InputStream inputStream = socket.getInputStream();
                    DataInputStream dis = new DataInputStream(inputStream);
                    while (true) {
                        try {
                            // 当读取数据的管道异常,说明客户端下线
                            String s = dis.readUTF();
                            // 发送给所有客户端
                            sendMsgAll(s);
                            System.out.println(socket.getRemoteSocketAddress() + ":" +s);
                        } catch (Exception e) {
                            socketList.remove(socket);
                            System.out.println("有人下线了:"+ socket.getRemoteSocketAddress());
                            dis.close();
                            socket.close();
                            break;
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
        }
        /**
         * 发送消息到所有客户端
         */ 
        private void sendMsgAll(String msg) throws Exception {
            for (Socket socket: socketList){
                OutputStream outputStream = socket.getOutputStream();
                DataOutputStream dos = new DataOutputStream(outputStream);
                dos.writeUTF(msg);
                dos.flush();
            }
        }
    }
}

 启动多个客户端就可以实现群聊效果了

4.4、实现BS架构

/**
 * BS服务端
 */
public class BSServer {

    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(8080);
        while (true){
            // 阻塞等待客户端连接
            Socket socket = serverSocket.accept();
            new Thread(new ServerThread(socket)).start();
        }
    }

    public static class ServerThread implements Runnable{
        private Socket socket;
        public ServerThread(Socket socket) {
            this.socket = socket;
        }
        @Override
        public void run() {
            try {
                OutputStream os = socket.getOutputStream();
                PrintStream ps= new PrintStream(os);
                    try {
                        ps.println("HTTP/1.1 200 OK");
                        ps.println("Content-Type:text/html; charset=UTF-8");
                        ps.println(); //必须换行
                        ps.println("网络编程666");
                        ps.println("<div style='color:red;'>JavaSE</div>");
                    } catch (Exception e) {
                        ps.close();
                        socket.close();
                    }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

启动之后在浏览器输入http://127.0.0.1:8080。就可以看到响应在浏览器的内容了

IO流

1、File

1.1、创建File

public class FileTest01 {
    public static void main(String[] args) {
        // 3种路径写法
        File f1 = new File("F:\\lintellij_code\\javase\\src\\main\\java\\io\\file\\a.txt");
        File f2 = new File("F:/lintellij_code/javase/src/main/java/io/file/a.txt");
        // f1.separator是调用系统的分隔符
        File f3 = new File("F:"+ f1.separator + "/lintellij_code/javase/src/main/java/io/file/a.txt");
        // 使用相对路径
        File f4 = new File("src/main/java/io/file/a.txt");
        System.out.println("f1.length():"+f1.length());
        System.out.println("f2.length():"+f2.length());
        System.out.println("f3.length():"+f3.length());
        System.out.println("f4.length():"+f4.length());

        // 可以指向文件和文件夹,也可以指向不存在的文件夹
        File f5 = new File("F:\\lintellij_code\\javase\\src\\main\\java\\io\\file");
        System.out.println("f5.length():"+f5.length()); // f5.length()是文件夹本身的大小
        System.out.println("f5.exists():"+f5.exists());
    }
}

推荐使用文件的相对路径,即src/...

1.2、File常用方法

1.3、前置知识

1.3.1、方法递归

1.3.2、文件搜索

public class FileSearch {
    public static void main(String[] args) throws IOException {
        searchFile(new File("E:/qq软件/Bin"), "QQScLauncher.exe");
    }

    public static void searchFile(File dir, String fileName) throws IOException {
        // 1、把非法的情况都拦截住
        if(dir == null || !dir.exists() || dir.isFile()) {
            return; //代表无法搜索
        }

        // 2、dir不是null,存在,一定是目录对象。
        // 获取当前目录下的全部一级文件对象。
        File[ ] files = dir.listFiles();

        // 3、判断当前目录下是否存在一级文件对象,以及是否可以拿到一级文件对象。
        if(files != null && files.length > 0){
        // 4、遍历全部一级文件对象。
            for (File f : files) {
                //5、判断文件是否是文件,还是文件夹
                if(f.isFile()){
                    //是文件,判断这个文件名是否是我们要找的
                    if(f.getName().contains(fileName)) {
                        System.out.println("找到了:" + f.getAbsolutePath());
                        //启动exe文件
                        Runtime runtime = Runtime.getRuntime();
                        runtime.exec(f.getAbsolutePath());
                    }
                }else {
                    //是文件夹,继续重复这个过程〈递归)
                    searchFile(f, fileName);
                }
            }
        }
    }
}
1.3.3、字符集

如果在idea中使用GBK编码UTF-8解码,就会出现乱码

2、IO流体系

输入流是要内存输入数据,输出流是内存将数据写到磁盘中

3、字节流

3.1、FileInputStream(文件字节输入流)

public class FileInputStreamTest01 {
    public static void main(String[] args) throws Exception {
        /**
         * 一次读取一个字节
         */
        // 两者路径写法
        // 1.创建文件字节输入流管道,于源文件接触
//        InputStream fileInputStream = new FileInputStream(new File("src/main/java/io/file/a.txt"));
        InputStream fileInputStream = new FileInputStream("src/main/java/io/file/a.txt");

        // 2.开始读取文件的字节数据
//        int a = fileInputStream.read();
//        System.out.println((char) a);

        // 调用系统资源来读取文件字节数据,性能极差,read()中文乱码,调用完要释放资源
        int b;
        while ((b = fileInputStream.read())  != -1){
            System.out.print((char) b);
        }
        // 释放资源
        fileInputStream.close();

        /**
         * 一次读取多个字节
         */
        // 一次读取多个字节
        InputStream is = new FileInputStream("src/main/java/io/file/a.txt");

        //2、开始读取文件中的字节数据:每次读取多个字节。
        byte[] buffer = new byte[3];
        //将字节读取到buffer桶中
        int len = is.read(buffer);
        //将buffer桶中的字节转换成字符串
        String rs = new String(buffer);
        System.out.println(rs);
        System.out.println("当次读取的字节数量:" + len);
        // 当次读取的字节数量: 3
        // buffer = [abc]
        // buffer = [66c]
        int len2 = is.read(buffer);
        //注意:读取多少,倒出多少。
        String rs2 = new String(buffer,0, len2);
        System.out.println(rs2);
        System.out.println("当次读取的字节数量: " + len2);
        int len3 = is.read(buffer);
        System.out.println(len3); // -1


        //3、使用循环改造。
        byte[] buffer1 = new byte[3];   // 每次读取3个字节(相当于桶)下次读取
        int len1;
        //记住每次读取了多少个字节。abc 66
        while ((len1 = is.read(buffer)) != -1){
            //注意:读取多少,倒出多少。
            String rs1 = new String(buffer1,0 , len1);
            System.out.print(rs1);
        }
        is.close();
        //性能得到了明显的提升!!
        //这种方案也不能避免读取汉字输出乱码的问题!!


        /**
         * 一次读整个文件的字节。文件字节输入流
         */
        InputStream is3 = new FileInputStream("src/main/java/io/file/a.txt");

//        File file3 = new File("src/main/java/io/file/a.txt");
        // 1、获取文件的字节数
//        long size = file3.length();
        //2、开始读取文件中的字节数据:每次读取多个字节。
//        byte[] buffer = new byte[(int) size];

        byte[] buffer3 = is.readAllBytes();  //两者获取文件字节长度的方法

        //将字节读取到buffer桶中
        is3.read(buffer3);
        //将buffer桶中的字节转换成字符串
        String str = new String(buffer3);
        System.out.println(str);
        is3.close();
    }
}

3.2、FileOutputStream(文件字节输出流)

/**
 * 字节输出流
 */
public class FileOutputStreamTest01 {
    public static void main(String[] args) throws Exception {

        //创建字符输出流通道(桶)覆盖式的写入,后面加了true参数,表示追加写入(非覆盖写入)
        // 路径不存在,则会自动创建
        OutputStream os = new FileOutputStream("src/main/resources/io-file/out.txt",true);

        os.write(97);   //97是字符a的ASCII码
        os.write("我爱Java----abc".getBytes());  //按照字符的字节写入
        os.write("我爱Java".getBytes(), 0, 6);  //按照字符的字节写入,从第0个字节开始,6个字节写入。
        os.write("\r\n".getBytes());    //添加换行符。\r支持各种平台,\n只支持windows平台

        os.close();
    }
}

案例

/**
 * 文件复制案例
 */
public class FileCopy {
    public static void main(String[] args) throws Exception {
        InputStream is = null;
        OutputStream os = null;

        try {
            is = new FileInputStream("src/main/resources/io-file/a.txt");
            os = new FileOutputStream("src/main/resources/io-file/a_copy.txt");
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer))!= -1) {
                os.write(buffer, 0, len);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // 关闭流,先创建的要后关闭
            os.close();
            is.close();
        }

        System.out.println("文件复制成功");
    }
}

4、释放资源的方式

防止代码出现异常,资源无法释放

public class FileCopy {
    public static void main(String[] args) throws Exception {
//        InputStream is = null;
//        OutputStream os = null;
//
//        try {
            System.out.println(10/0);
//            is = new FileInputStream("src/main/resources/io-file/a.txt");
//            os = new FileOutputStream("src/main/resources/io-file/a_copy.txt");
//            byte[] buffer = new byte[1024];
//            int len;
//            while ((len = is.read(buffer))!= -1) {
//                os.write(buffer, 0, len);
//            }
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        } finally {
//            // 关闭流,先创建的要后关闭
//            if (os != null) os.close();
//            if (os != null) is.close();
//        }
//
//        System.out.println("文件复制成功");
//    }

        /**
         * 推荐使用try-with-resources释放资源
         */
        try(
                InputStream is = new FileInputStream("src/main/resources/io-file/a.txt"); 
                OutputStream os = new FileOutputStream("src/main/resources/io-file/a_copy.txt")
        ) 
        {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer))!= -1) {
                os.write(buffer, 0, len);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

5、字符流

字符流非常适合做文件的读写

5.1、FileReader(文件字符输入流)

/**
 * 字节输出流
 */
public class FileOutputStreamTest01 {
    public static void main(String[] args) throws Exception {

        try(Reader os = new FileReader("src/main/resources/io-file/out.txt")) {
            /**
             * 一个字符一个字符的读取
             */
            int c ;
            while ((c=os.read())!= -1) {
                System.out.print((char) c); // 将ASCII码转为字符
            }

            /**
             * 多个字符读取
             */
            char[] buffer = new char[3];
            int len;
            while ((len=os.read(buffer))!= -1) {
                //将字节转换为字符
                System.out.print(new String(buffer, 0, len));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}

5.2、FileWriter(文件字符输出流)

public class FileWired {
    public static void main(String[] args) {

        // 参数append:true表示追加写入
        try(Writer wf = new FileWriter("src/main/resources/io-file/writer.txt", true)) {
            wf.write("a");  // 写入一个字符
            wf.write("中文");
            wf.write(97);   // 根据ASCII码写入一个字符
            wf.write("abc中文");  // 写入一个字符串
            wf.write("abc中文",0,4);  // 写入字符串的一部分
            wf.write("\r\n");   // 写入一个换行符

            char[] chars = {'a', 'b', 'c', '加', '瓦'};
            wf.write(chars);  // 写入一个字符数组

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

6、缓冲流

6.1、字节缓冲流

字节缓冲流有BufferedInputtStream字节缓冲输入流和BufferedOutputStream字节缓冲输出流

/**
 * 字节缓冲流
 */
public class BufferedTest {
    public static void main(String[] args) throws Exception {
        try(
                InputStream is = new FileInputStream("src/main/resources/io-file/a.txt");
                InputStream bis  = new BufferedInputStream(is);
                OutputStream os = new FileOutputStream("src/main/resources/io-file/a_copy.txt");
                // 默认的缓冲区大小是8k,也可以设置比如9k
                OutputStream bos = new BufferedOutputStream(os, 1024*9);
        )
        {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer))!= -1) {
                bos.write(buffer, 0, len);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

6.2、BufferedReader(字符缓冲流)

        /**
         * 字符缓存输入流
         */
        try(
                Reader fr = new FileReader("src/main/resources/io-file/a.txt");
                // 包装流,包装一个字符流
                BufferedReader br  = new BufferedReader(fr);
                // 默认的缓冲区大小是8k,也可以设置比如9k
        )
        {
            byte[] buffer = new byte[1024];
            String str;
            while ((str = br.readLine()) != null) {
                System.out.println(str);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

6.3、BufferedWriter字节输出流

6.4.案例

public class BufferedCharDemo {
    public static void main(String[] args) {
        try(
                FileReader fr = new FileReader("src/main/resources/io-file/出师表.txt");
                FileWriter fw = new FileWriter("src/main/resources/io-file/出师表-copy.txt");
                BufferedReader br = new BufferedReader(fr);
                BufferedWriter bw = new BufferedWriter(fw)
                ) {

            List<String> data = new ArrayList<>();

            String str;
            while ((str = br.readLine()) != null){
                data.add(str);
            }

            // 按照元素的第首数字排序
            Collections.sort(data);

            for (String line : data){
                bw.write(line);
                bw.newLine();
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

7、转换流

/**
 * 字符输入流转换为字节输入流
 */
public class InputStreamReaderTest {
    public static void main(String[] args) {
        try(
                InputStream fis = new FileInputStream("src/main/resources/io/转换流.txt");
                // 转换流,将字节流转换为字符流
                InputStreamReader isr = new InputStreamReader(fis, "GBK");
                BufferedReader br = new BufferedReader(isr)
                ) {
                    String line;
                    while ((line = br.readLine())!= null) {
                        System.out.println(line);
                    }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意编码的方式要和文件的编码一样

/**
 * 字符输出流转换为字节输出流
 */
public class OutputStreamWriterTest {
    public static void main(String[] args) {
        try(
                OutputStream fos = new FileOutputStream("src/main/resources/io/转换流.txt");
                // 转换流,将字节流转换为字符流
                OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");
                BufferedWriter bw = new BufferedWriter(osw)
                ) {
            bw.write("加瓦太难了");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

8、打印流

打印流几乎可以替代前面所有的写操作(这是一个字节输出流),且自带缓冲流,性能较好

其实System.out.println()语句就是打印流,默认打印位置是控制台

9、数据流

/**
 * 数据输出流
 */
public class DataOutputStreamTest {
    public static void main(String[] args) {
        String filePath = "src/main/resources/io/数据流.txt";
        try(DataOutputStream dos = new DataOutputStream(new FileOutputStream(filePath))) {
            dos.writeInt( 97);
            dos.writeDouble(99.5);
            dos.writeBoolean( true);
            dos.writeUTF("这是中文");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这不是乱码,只是方便下次读取

/**
 * 数据输出流
 */
public class DataInputStreamTest {
    public static void main(String[] args) {
        String filePath = "src/main/resources/io/数据流.txt";
        try(DataInputStream dos = new DataInputStream(new FileInputStream(filePath))) {

            System.out.println(dos.readInt());
            System.out.println(dos.readDouble());
            System.out.println(dos.readBoolean());
            System.out.println(dos.readUTF());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

写和读的操作应该一一对应,否则报错

10、序列化流

对象序列化:把Java对象写入到文件中去
对象反序列化:把文件里的Java对象读出来

// 需要序列化的对象一定要实现Serializable接口
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    private String loginName;
    private String username;
    private int age;
    // transient关键字修饰的变量不参与序列号
    private transient String password;
}



/**
 * 序列化对象
 */
public class ObjectOutputStreamTest {
    public static void main(String[] args) {
        String path = "src/main/resources/io流/序列号流.txt";

        try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path))) {
            User user = new User("admin", "张三", 32, "java666");
            // 序列号对象到文件中
            oos.writeObject(user);
            System.out.println("对象序列号成功");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}



/**
 * 反序列化
 */
public class ObjectInputStreamTest01 {
    public static void main(String[] args) {
        String path = "src/main/resources/io流/序列号流.txt";

        try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path))) {
            // 序列号对象到文件中
            ObjectOutputStreamTest01.User u = (ObjectOutputStreamTest01.User) ois.readObject();
            System.out.println(u);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

11、IO框架

日志

1、概述

2、Logback日志

2.1、Logback日志介绍

2.2、Logback日志快速入门

导入logback依赖坐标

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.12</version>
        </dependency>

 我的是maven项目,在resources目录下新建logback.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

    <!-- 控制台输出Appender -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- 日志输出格式 -->
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 文件输出Appender -->
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <!-- 日志文件路径 -->
        <file>F:/lintellij_code/javase/src/main/resources/logback.log</file>
        <append>true</append>
        <encoder>
            <!-- 日志输出格式 -->
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>

        <!--指定日志文件拆分和压缩规则-->
        <rollingPolicy class="ch.qos.logback.core.rolling.sizeAndTimeBasedRollingPolicy">
            <!--通过指定压缩文件名称,来确定分割文件方式,文件到达1MB就压缩,文件名格式为:logback-年-月-日.log.gz-->
            <fileNamePattern>F:/lintellij_code/javase/src/main/resources/logback-%i-%d{yyyy-MM-dd}-.log.gz</fileNamePattern>
            <!--文件拆分大小-->
            <maxFilesize>1MB</maxFilesize>
        </rollingPolicy>

    </appender>

    <!-- 指定包(com.example)的日志级别为DEBUG -->
    <logger name="com.example" level="DEBUG" />

    <!-- 根Logger,info表示只输出info及以上级别的日志,all表示将root标签下的所有日志都输出,-->
    <root level="info">
        <!-- 控制台输出-->
        <appender-ref ref="CONSOLE" />
        <!-- 文件输出-->
        <appender-ref ref="FILE" />
    </root>

</configuration>

编写测试代码

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogBackTest {
    // 创建一个日志对象,LogBackTest.class是这个日志对象的名字
    private final static Logger LOGBACK = LoggerFactory.getLogger(LogBackTest.class);
    public static void main(String[] args) {
        try {
            LOGBACK.info("开始执行1/0");
            int a = 1/0;
            LOGBACK.info("1/0执行完了");
        } catch (Exception e) {
            LOGBACK.error("1/0出bug了~~~");
        }
    }
}

运行完后就可以看到日志信息保存到了logback.log文件中了

2.3、核心配置文件logback.xml

 

面向对象

1、枚举 

public class EnumTest {
    public static void main(String[] args) {
        System.out.println("code:" + E.A.getCode() + ",msg:" + E.A.getMsg());
        System.out.println(E.C);
        System.out.println("年龄:" + E.age);
    }


    public enum E {

        // 枚举成员变量,要按照类似构造函数的形式进行初始化,然后使用get方法获取里面的值
        A(1, "hello"),
        B(2, "world"),
        C;

        // 常规变量
        public static int age = 22;

        private int code;
        private String msg;

        // 构造方法
        E(int code, String msg) {
            this.code = code;
            this.msg = msg;
        }
        E(){}

        public int getCode() {
            return code;
        }

        public String getMsg() {
            return msg;
        }
    }
}

2.、泛型

        定义类、接口、方法时,同时声明了一个或者多个类型变量(如:<E>),称为泛型类、泛型接口,泛型方法、它们统称为泛型,
问题和注意事项:
        泛型的擦除泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了,这就是泛型擦除。
泛型不支持基本数据类型,只能支持对象类型(引用数据类型)。

public class FanXingTest {
    public static void main(String[] args) {
        Fanxing<String,Integer> fanxing = new Fanxing<>();
        fanxing.add1("a", 1);
        fanxing.add2(5);
        ArrayList<StringNumber> list = new ArrayList<>();
        fanxing.add3(list);
    }
    public static class Fanxing<E,F>{
        public void add1(E e,F f){
            System.out.println(e  + "e是java类的泛型" + f);
        }
        public <A> void add2(A e){
            System.out.println("e是自定义泛型方法:"+ e.getClass());
        }
        // ?泛型通配符,但是一定是Number及其的子类
        public void add3(ArrayList<? extends Namber> list){
            System.out.println(list);
        }
    }
    public static class Namber{}
    public static class StringNumber extends Namber{}
}

异常Exception

入门异常

编译时的异常,IDEA会报红:

可以加 throws ParseException 将异常抛给上一层(main方法的上一层,即jvm),jvm捕捉异常后打印到控制台,也可以使用 try-catch 包围,选中之后 ctrl + alt + t 

自定义运行时异常

public class exceptionTest02 {
    public static void main(String[] args) {
        // 使用try-catch语句来捕获异常
        try {
            saveAge(10);
            System.out.println("底层执行常规");
        } catch (Exception e) {
            // 打印异常信息
            e.printStackTrace();
            System.err.println("底层执行常规");
        }
    }

    public static void saveAge(int age){
        if (age > 0 && age < 150){
            System.out.println("年龄被保存了---" + age);
        }else {
            //使用自定义异常类
            // throw 关键字将异常对象抛出给上层
            throw new AgeIllegalRuntimeException("年龄不合法");
        }
    }

    // 自定义异常类,继承RuntimeException,
    public static class AgeIllegalRuntimeException extends RuntimeException{
        // 重写构造器
        public AgeIllegalRuntimeException(String msg) {
            super(msg);
        }
    }
}

自定义编译时异常

public class exceptionTest03 {
    public static void main(String[] args) {
        // 使用try-catch语句来捕获异常,不抛异常idea就会爆红
        try {
            saveAge(10);
            System.out.println("底层执行常规");
        } catch (Exception e) {
            // 打印异常信息
            e.printStackTrace();
            System.err.println("底层执行常规");
        }
        
    }

    public static void saveAge(int age) throws AgeIllegalException {
        if (age > 0 && age < 150){
            System.out.println("年龄被保存了---" + age);
        }else {
            //使用自定义异常类
            // throw 关键字将异常对象抛出给上层
            throw new AgeIllegalException("年龄不合法");
        }
    }
    
    
    // 自定义编译时异常类,继承Exception,
    public static class AgeIllegalException extends Exception{
        // 重写构造器
        public AgeIllegalException(String msg) {
            super(msg);
        }
    }
}

错误严重的就使用编译时异常,错误不严重的就使用运行时异常

异常处理的两种方式

1、埔获异常,记录异常苷响应合适的信息给用户

public class exceptionTest04 {
    public static void main(String[] args) {
        try {
            test();
        } catch (Exception e) {
            System.out.println("您当前操作有问题");
            e.printStackTrace();
        }
    }

    //全部异常都可以用Exception来抛Exception是所有异常的祖宗
    public static void test() throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        Date date = sdf.parse("2023-12-15 9:02");
        System.out.println(date);
        f();
    }

    public static void f() throws Exception {
        // 读取文件的
        InputStream inputStream = new FileInputStream("D/meinv.png");
    }

}

2、捕获异常,尝试重新修复

public class exceptionTest05 {
    public static void main(String[] args) {
        //需求:调用一个方法,让用户输入一个合适的价格返回为止
        // 尝试修复,即使输入文字系统也不会崩
        while (true) {
            try {
                System.out.println(getMoney());
                break;
            }catch (Exception e) {
                System.out.println("请您输入合法的数字!!");
            }
        }
    }
    public static double getMoney(){
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请您输入合适的价格:");
            double money = sc.nextDouble();
            if(money >= 0){
                return money;
            }else {
                System.out.println("您输入的价格是不合适的");
            }
        }
    }

}

反射

1、认识反射

对加载在内存中的类进行操作

2、获取class对象(字节码对象)

/**
 * 获取class对象
 */
public class GetClassTest {
    public static void main(String[] args) throws Exception {
        // 获取class对象
        Class c1 = Student.class;   // 全类名
        System.out.println(c1.getName());
        Class c2 = Class.forName("反射.Student");
        System.out.println(c1==c2);

        Student student = new Student();
        Class c3 = student.getClass();
        System.out.println(c2==c3);
    }
}

3、获取Class的构造器对象

/**
 * 获取构造器并使用
 */
public class GetConstructor {
    public static void main(String[] args) throws Exception {
        //1、反射第一步:必须先得到这个类的Class对象
        Class c = Student.class;
        //2、获取某个构造器:无参数构造器
        // constructor constructor = c.getConstructor( );
        Constructor constructor1 = c.getDeclaredConstructor();
        System.out.println(constructor1.getName() + "--->"+ constructor1.getParameterCount());
        // 创建对象
        Student s1 = (Student) constructor1.newInstance();
        System.out.println(s1);

        //3、获取有参数构造器
        Constructor constructor2 = c.getDeclaredConstructor(String.class, int.class);
        System.out.println(constructor2.getName() + "--->" + constructor2.getParameterCount());

        //4、创建对象
        constructor2.setAccessible(true);   // 禁止检查访问权限
        Student s2 = (Student) constructor2.newInstance("张三", 20);
        System.out.println(s2);
    }
}

4、获取成员变量

/**
 * 获取成员变量
 */
public class GetField {
    public static void main(String[] args) throws Exception {
        //1、反射第一步:必须是先得到类的Class对象
        Class c = Cat.class;
        //2、获取类的全部成员变量。
        Field[] fields = c.getDeclaredFields();
        //3、遍历这个成员变量数组
        for (Field field : fields) {
            System.out.println(field.getName() +"--->"+ field.getType());
        }
        //4、定位某个成员变量
        Field fName = c.getDeclaredField("name");
        System.out.println(fName. getName() + "--->" + fName.getType());
        Field fAge = c.getDeclaredField("age" );
        System.out.println(fAge.getName() + "--->" + fAge.getType());
    }
}

5、获取成员方法

/**
 * 获取成员方法并执行
 */
public class GetMethods {
    public static void main(String[] args) throws Exception {
        //1、反射第一步:先得到Class对象。
        Class c = Cat.class;
        //2、获取类的全部成员方法。
        Method[] methods = c.getDeclaredMethods();
        //3、遍历这个数组中的每个方法对象
        for (Method method : methods) {
            System.out.println(method.getName() + "--->" +method.getParameterCount()+"---->"+method.getReturnType());
        }
        //4、获取某个方法对象
        Method run = c.getDeclaredMethod("run");
        Method p = c.getDeclaredMethod("p", String.class);
        //拿run方法,无参数的
        System.out.println(run. getName() + "--->"+run.getParameterCount() + "_--->"+run.getReturnType());
        System.out.println(p. getName() + "--->"+p.getParameterCount() + "_--->"+p.getReturnType());

        Cat cat = new Cat();
        //调用无参数的run方法,用cat对象触发调用的
        Object rs = run.invoke(cat);  // rs是执行方法的返回结果
        System.out.println("run方法的返回值:" + rs);
        p.setAccessible(true); //禁止检查访问权限
        Object s = p.invoke(cat,"aa");
        System.out.println(s);
    }
}

6、使用反射制作Java框架

@Data
@AllArgsConstructor
@NoArgsConstructor
class Student {
    private String name;
    private int age;
    private char sex;
    private double height;
    private String hobby;
}


@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private String name;
    private double salary;  //薪水
}


/**
 * 目标:保存任意对象的字段和其数据到文件中去
 */
public class ObjectFrame {
    public static void saveObject(Object obj) throws Exception {
        String filePath = "src/main/java/反射/制作简易框架案例/data.txt";
        PrintStream ps = new PrintStream(new FileOutputStream(filePath,true));

        Class c = obj.getClass();
        String cName = c.getSimpleName();   // 获取类名
        ps.println("---------------" + cName + "----------------");
        //2、从这个类中提取它的全部成员变量
        Field[] fields = c.getDeclaredFields();
        //3、遍历每个成员变量。
        for (Field field : fields) {
            //4、拿到成员变量的名字
            String name = field.getName();
            //5、拿到这个成员变量在对象中的数据。
            field.setAccessible(true); //禁止检查访问控制
            String value = field.get(obj) +"";
            //6、写入文件
            ps.println(name + "=" + value);
        }
        ps.close();
    }
}


public class FrameTest {
    public static void main(String[] args) throws Exception {
        Student s1 = new Student("小飞吴彦祖", 18, '男', 180, "篮球");
        Teacher t1 = new Teacher("小美",50000);
        ObjectFrame.saveObject(s1);
        ObjectFrame.saveObject(t1);
    }
}

注解

1、自定义注解

/**
 * 自定义注解
 */
public @interface MyAnnotation {
    String a();
    boolean b() default true;
    String[] c();
}


@MyAnnotation(a = "张三", c={"1", "2", "3"})
public class AnnotationTest {

    @Test
    @MyAnnotation(a = "李四", b = false, c={"HTML", "CSS"})
    public void test() {
        System.out.println("test");
    }
}

2、注解的原理

运行代码就可以生成AnnotationTest类的class文件

然后再这个目录中cmd,输入命令>javap class文件名(不带后缀)我是:javap MyAnnotation
进行反编译:


里面都是抽象方法

3、元注解

4、注解的解析

5、注解的应用,模拟Junit框架

/**
 * 自定义注解
 */
@Target({ElementType.TYPE, ElementType.METHOD}) // 注解的作用范围是类和方法上
@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期是运行时
public @interface MyTest4 {
    String value();
    double aaa() default 100;
    String[] bbb();
}


/**
 * 使用注解
 */
@MyTest4(value = "老六", aaa = 10,bbb = {"111", "222"})
public class Demo {
    @MyTest4(value = "老7", aaa = 15,bbb = {"333", "444"})
    public void test(){
        System.out.println("这是一个方法");
    }
}


/**
 * 解析注解
 */
public class AnnotationTest {
    public static void main(String[] args) throws Exception {
        //1、先得到Class对象
        Class c = Demo.class;
        //2、解析类上的注解
        //判断类上是否包含了某个注解
        if(c.isAnnotationPresent(MyTest4.class)){
            //获取全部注解
            MyTest4 myTest4 = (MyTest4) c.getDeclaredAnnotation(MyTest4.class);
            // 获取注解的值
            System.out.println(myTest4.value());
            System.out.println(myTest4.aaa());
            System.out.println(Arrays.toString(myTest4.bbb()));
        }


        System.out.println("------------------------");
        //解析方法上的注解
        Method m = c.getDeclaredMethod("test");
        if (c.isAnnotationPresent(MyTest4.class)){
            MyTest4 myTest4 = (MyTest4) m.getDeclaredAnnotation(MyTest4.class);
            System.out.println(myTest4.value());
            System.out.println(myTest4.aaa());
            System.out.println(Arrays.toString(myTest4.bbb()));
        }

    }
}

junit框架中的@Test注解在IDEA中是会有启动按钮的,而我的模拟的注解是没有的,那是因为我没有和IDEA的公司打电话,嘿嘿!

/**
 * 自定义注解
 */
@Target(ElementType.METHOD) //注解只能注解方法
@Retention(RetentionPolicy.RUNTIME) //让当前注解可以一直存活着
public @interface MyTest {
}


/**
*目标:模拟Junit框架的设计。
 */
public class AnnotationTest {
    public void test1(){
        System.out.println( " ===test1====" );
    }
    @MyTest
    public void test2(){
        System.out.println( "===test2====");
    }

    public static void main(String[] args) throws Exception {
        AnnotationTest a = new AnnotationTest();
        //启动程序!
        //1、得到Class对象
        Class c = AnnotationTest.class;
        //2、提取这个类中的全部成员方法
        Method[ ] methods = c.getDeclaredMethods();
        //3、遍历这个数组中的每个方法,看方法上是否存在@MyTest注解,存在
        // 触发该方法执行。
        for (Method method : methods) {
            if(method.isAnnotationPresent(MyTest.class)){
                //说明当前方法上是存在@MyTest触发当前方法执行。
                method.invoke(a);
            }
        }
    }
}

动态代理

/**
 * 给中介公司用的代理接口
 */
public interface Star {
    String sing(String name);
    void dance();
}


**
 *  大明星类
 */
public class BigStar implements Star{
        private String name;
        public BigStar (String name) {
            this.name = name;
        }

        // 唱歌
        @Override
        public String sing (String name){
            System.out.println(this.name + "正在唱: " +name) ;return "谢谢!谢谢! ";
        }

        // 跳舞
        @Override
        public void dance(){
            System.out.println(this.name+"正在优美的跳舞~~");
        }
}


/**
 * 代理公司
 */
public class ProxyUtil {
    public static Star createStar(BigStar bigStar){
        /** Proxy.newProxyInstance方法的参数:他可以创建一个代理对象
         * ClassLoader loader,
         * Class<?>[] interfaces,
         * InvocationHandler h
         */

        //一般在主程序中这样调用:
        // Star starProxy = ProxyUtil.createProxy(s);
        // StarProxy.sing("好日子") starProxy.dance();
        // 1. 动态代理
        Star starProxy = (Star) Proxy.newProxyInstance(
                ProxyUtil.class.getClassLoader(), new Class[]{Star.class}, new InvocationHandler() {
                    /**
                     * 代理对象调用方法时,会调用invoke方法
                     * Object proxy:当前调用对象:starProxy
                     * Method method:当前调用的方法
                     * Object[] args:当前调用方法的参数
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (method.getName().equals("sing")){
                            System.out.println("准备话筒,收钱200万");
                        } else if (method.getName().equals("dance")) {
                            System.out.println("准备场地,收钱500万");
                        }
                        // 2. 调用目标对象的方法
                        return  method.invoke(bigStar, args);
                    }
                });

        return starProxy;
    }
}


public class Test {
    public static void main(String[] args) {
        BigStar bigStar = new BigStar("杨超越");
        Star star = ProxyUtil.createStar(bigStar);
        String r = star.sing("好日子");
        System.out.println(r);
        star.dance();
    }
}

  • 25
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值