Java之反射什么是反射Java中的反射（Reflection）是指在程序运行时动态地获取类的信息（如类名、方法、属性等），并能够操作对象的方法和属性，而不需要预先知道这些信息。反射使得程序具有更高的灵活性和可扩展性，但也会带来一定的性能开销。反射的种类类名说明Field类类的成员变量Method类类的方法Constructor类的构造方法反射获取Class对象的三种方式Class.forName("全类名");类名.class对象.getClass();package Demo; public class Person { String name; int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }package Demo; import org.junit.Test; public class ReflectionTest { //第一种方式:Class.forName // 全类名： 包名+类名 (全类名) 通常用第一种方式 @Test public void show() throws ClassNotFoundException { //Class.forName("全类名").var Class<?> aClass = Class.forName("Demo.Person"); System.out.println(aClass); //class Demo.Person } //第二种方式：类名.class // 通常在多线程中使用 用来当对象锁 synchronized @Test public void show1() { //Person.class.var Class<Person> personClass = Person.class; System.out.println(personClass); //class Demo.Person } //第三种方式： 对象.getClass //有了这个类当对象的时候才可以使用 @Test public void show2() { //先实例化对象 通过对象去调用getClass Person p = new Person(); //p.getClass().var Class<? extends Person> aClass = p.getClass(); System.out.println(aClass); //class Demo.Person } }反射获取构造方法属性说明newInstance创建对象getConstructors获取公共的构造方法getDeclaredConstructor获取空参的构造方法getDeclaredConstructors获取所有的构造方法public class Person { String name; private int age; public Person() { } public Person(String name) { this.name = name; } //受保护的构造方法 protected Person(int age) { this.age = age; } //私有构造方法 通过暴力反射可以进行访问 private Person(String name, int age) { this.name = name; this.age = age; } }public class ReflectionTest02 { public static void main(String[] args) throws Exception { //1.获取Class字节码文件对象 Class aClass = Class.forName("Demo.Person"); //2.获取公共的构造方法 Constructor[] constructors = aClass.getConstructors(); //3.获取所有的构造方法 Constructor[] declaredConstructors = aClass.getDeclaredConstructors(); //4.获取空参的构造方法 Constructor dc = aClass.getDeclaredConstructor(); //5.获取Sring构造方法 Constructor dc1 = aClass.getDeclaredConstructor(String.class); //6.获取int构造方法 Constructor dc2 = aClass.getDeclaredConstructor(int.class); //7.获取String和int构造方法 Constructor dc3 = aClass.getDeclaredConstructor(String.class, int.class); //8.通过7来创建对象 //setAccessible：暴力反射 取消权限检验 dc3.setAccessible(true); //创建对象 Person person = (Person) dc3.newInstance("阿乐的小屋", 22); System.out.println(person); } }反射获取成员变量属性说明getFields获取公开的成员变量对象getDeclaredFields获取所有成员变量对象getDeclaredField获取单个成员变量getModifiers获取权限修饰符getName获取成员变量的名字getType获取成员变量的数据类型setAccessible取消修饰的权限检测public class Person { private String name; private int age; public int id; public Person() { } public Person(String name, int age, int id) { this.name = name; this.age = age; this.id = id; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", id=" + id + '}'; } }public class ReflectionTest02 { public static void main(String[] args) throws Exception { //1.获取Class字节码文件的对象 Class aClass = Class.forName("Demo.Person"); //2.获取公开的成员变量的对象 Field[] fields = aClass.getFields(); //public int Demo.Person.id 数组 //3.获取所有的成员变量对象 Field[] fields1 = aClass.getDeclaredFields(); //4.获取单个成员变量 Field name = aClass.getDeclaredField("name"); //5.获取权限修饰符 int modifiers = name.getModifiers(); //2 //6.获取成员变量的名字 String name1 = name.getName(); //name //7.获取成员变量的数据类型 Class<?> type = name.getType(); //class java.lang.String //8.获取成员变量记录的值 Person p = new Person("blogdx.com",22,1); name.setAccessible(true); String value = (String) name.get(p); //blogdx.com //修改对象里面记录的值 name.set(p,"阿乐的小屋"); //Person{name='阿乐的小屋', age=22, id=1} //获取成员变量int，将int修改为20 //获取age的成员变量 Field age = aClass.getDeclaredField("age"); //暴力反射取消权限检测 age.setAccessible(true); //读取age的值 int i1 = (int) age.get(p); //22 //修改age的值 age.set(p,20); System.out.println(p); //Person{name='阿乐的小屋', age=20, id=1} } }反射获取成员方法属性说明getMethods获取所有方法对象包含父类方法getDeclaredMethods获取本类所有方法对象包含私有方法getDeclaredMethod获取指定单一方法getModifiers获取方法的修饰符getName获取方法的名字getParameters获取方法的形参getExceptionTypes获取方法抛出的异常invoke("实例","参数")指定实例运行方法public class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void sleep(){ System.out.println("睡觉"); } //私有方法 private void eat (String name) throws RuntimeException,NullPointerException{ System.out.println(name + "在吃东西"); } //公开方法有返回值 public String fly(String name){ System.out.println(name + "在飞翔"); return "在飞翔"; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }public class ReflectionTest02 { public static void main(String[] args) throws Exception { //获取class字节码文件对象 Class aClass = Class.forName("Demo.Person"); //1.获取所有的方法对象(包含父类的公共方法) Method[] methods = aClass.getMethods(); //2.获取本类里面的所有方法和对象(包含私有的方法) Method[] methods1 = aClass.getDeclaredMethods(); //3.获取指定的单一方法 Method m = aClass.getDeclaredMethod("eat", String.class); //private void Demo.Person.eat(java.lang.String) //4.获取方法的修饰符 int modifiers = m.getModifiers(); //2 //5.获取方法的名字 String name = m.getName(); System.out.println(name); //6.获取方法的形参 Parameter[] parameters = m.getParameters(); //java.lang.String arg0 //7.获取方法抛出的异常 Class<?>[] exceptionTypes = m.getExceptionTypes();//class java.lang.RuntimeException class java.lang.NullPointerException //8.运行方法 Person p = new Person(); m.setAccessible(true); //p是方法的调用者 //"张三" 在调用方法的时候传入的参数 m.invoke(p,"张三"); //9.获取方法的返回值 Method f = aClass.getDeclaredMethod("fly", String.class); Person pp = new Person(); //返回值 String bird = (String) f.invoke(pp, "鸟儿"); System.out.println(bird); //在飞翔 } }反射的好处作用：获取任意一个类中的所有信息 结合配置文件动态创建对象动态性：反射允许程序在运行时动态地获取类的信息，从而可以根据需要创建对象、调用方法、访问属性等。灵活性：通过反射可以访问类的私有成员，以及无法直接访问的成员，从而增强了程序的灵活性。扩展性：反射机制使得程序可以利用外部的类或者对象，而不需要在编译时就将其引入到程序中。逆向工程：反射允许程序在运行时检查类的结构，进而进行逆向工程，例如生成Java代码、序列化和反序列化等。框架开发：许多框架都使用了反射，例如Spring框架就利用反射来实现依赖注入、AOP等功能。这些框架因此具有高度的灵活性和可扩展性。作业练习创建一个Person类，用反射创建对象并且将内容保存到本地文本中public class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }public class PersonTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException { //1.获取字节码文件 Class aClass = Class.forName("Demo.Person"); //2.创建缓冲流 BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt")); //3.获取成员变量 Field[] fields = aClass.getDeclaredFields(); for (Field field : fields) { //4.获取构造方法 Constructor con = aClass.getDeclaredConstructor(String.class, int.class); Person p = (Person) con.newInstance("阿乐的小屋",22); field.setAccessible(true); bw.write(field.getName() + "=" + field.get(p)); bw.newLine(); } bw.close(); } }通过 Class.forName() 方法获取类 Demo.Person 的字节码文件。创建缓冲流BufferedWriter 并指定输出文件 a.txt。通过 aClass.getDeclaredFields() 方法获取类 Demo.Person 中所有声明的成员变量。遍历 fields 数组，对于每个成员变量，通过 getDeclaredConstructor() 方法获取构造方法，然后利用 newInstance() 方法创建一个新的 Person 对象。调用 field.setAccessible(true) 方法，使得可以访问非公有成员变量。然后利用 field.getName() 和 field.get() 方法分别获取成员变量的名称和值，并将其写入文件 bw 中。关闭缓冲流 bw。创建一个properties配置文件 动态的调用配置文件中的方法输出到控制台 配置文件内容如下classname=Demo.Person method=showpublic class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } //方法一 public void show(){ System.out.println("阿乐在学习"); } //方法二 public void run(){ System.out.println("阿乐的小屋"); } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }public class PersonTest02 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException { //1.创建一个io流读取配置文件 FileInputStream fis = new FileInputStream("prop.properties"); Properties prop = new Properties(); //将配置文件加载到prop中 prop.load(fis); //关闭流 fis.close(); //prop.get获取键 String classname = (String) prop.get("classname"); String method = (String) prop.get("method"); //3.获取class字节码对象 Class<?> aClass = Class.forName(classname); //4.获取方法 Method show = aClass.getDeclaredMethod(method); //4.实例化Person类 Person p = new Person(); //运行方法 show.invoke(p); } }通过 FileInputStream 类创建一个输入流对象 fis，用于读取配置文件 "prop.properties"。创建 Properties 对象 prop，将配置文件加载到 prop 中。这样就可以使用 prop.get 方法获取键对应的值（类名和方法名）。调用 Class.forName 方法获取指定类名 classname 的 Class 对象 aClass。使用 aClass.getDeclaredMethod 方法获取指定方法名 method 的 Method 对象 show。实例化 Person 类并用 show.invoke(p) 方法调用指定的方法。
多线程在一个程序中同时执行多个线程，每个线程独立执行自己的代码片段，并且共享该程序的内存空间和系统资源相关概念什么是程序程序是一组指令和数据的集合，用于描述计算机执行特定任务的过程什么是进程进程就是正在运行的程序（进程是驻留在内存中的）每一个进程都有自己的PID什么是线程一个进程至少有一个线程。线程是程序内部的一条执行路径什么是多线程单线程吃饭的时候 先接电话 接完了以后在继续吃饭。（优雅）程序只有一个执行路径，所有的代码都是顺序执行的多线程人一边听歌一边抽烟一边打游戏 一心三用。（牛逼）每个线程都是独立执行的代码片段，程序通过启动多个线程来并发执行不同的任务或操作多线程的优点提高程序的执行效率和响应速度提高用户体验和交互性支持复杂的程序逻辑和业务需求充分利用计算机的多核 CPU 和并发处理能力提高程序的可靠性和安全性什么是并行、并发并行多个任务或操作同时执行，并在同一时间段内完成（多个事件在同一时刻发生）并发执行多个独立的任务或线程（100个人抢10张火车票、1000个人一起访问你的网站）线程的创建和启动1.1继承thread类以下示例中one类来继承Thread类并重写run()方法 表示创建了一个线程通过实例化调用start方法进行启动线程当运行这段程序时,就可以看到两个线程分别交替打印出1到100的奇数和偶数了Thread.currentThread().getName()是Thread类的静态方法，调用该方法可以获取当前执行线程的名称this.getName()是实例方法，只能在当前线程对象中使用总之 都可以用于获取当前线程的名称public class TestDemo { public static void main(String[] args) { //将线程进行实例化 one o = new one(); two t = new two(); //启动线程 o.start(); t.start(); } } //打印偶数 class one extends Thread{ @Override public void run() { for (int i = 0; i <= 50; i++) { if (i % 2 ==0) { //获取线程的名称 System.out.println(Thread.currentThread().getName() + "--->" + i); } } } } //打印奇数 class two extends Thread{ @Override public void run() { for (int i = 0; i <= 50; i++) { if (i % 2 != 0) { //获取线程的名称 System.out.println(this.getName() + ">>>" + i); } } } }1.2通过匿名内部类创建线程有很多时候我们在测试的时候没有时间创建好几个类进行测试 可以使用匿名内部类 这样比较方便new Thread(){}.start();大括号中重写run方法 在写代码块public class TestDemo2 { public static void main(String[] args) { new Thread(){ @Override public void run() { for (int i = 0; i <= 10; i++) { if (i % 2 != 0) { System.out.println(this.getName() + "奇数" + i); } } } }.start(); new Thread(){ @Override public void run() { for (int i = 0; i <= 10; i++) { if (i % 2 == 0){ System.out.println(this.getName() + "偶数：" + i); } } } }.start(); new Thread(){}.start(); } }2.1实现Runnable接口以下示例中TestDemo4类实现了Runnable接口重写run方法TestDemo4 当前实现类的对象将实现类的对象作为参数传递到Thread构造器当中public class TestDemo4 implements Runnable{ public static void main(String[] args) { //创建实现类对象 TestDemo4 td = new TestDemo4(); //将对象作为参数传递到Thread类的构造器中 创建Thread的实例 线程0 Thread t = new Thread(td); t.start(); //主线程 for (int i = 0; i <= 10; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } } //在创建一个线程用来遍历偶数 线程1 Thread t1 = new Thread(td); t1.start(); } @Override public void run() { for (int i = 0; i <= 10; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } } } }2.2通过匿名内部类创建线程new Thread(new Runnable() {}).start();在括号内添加new Runnable()并重写run方法就可以了public class RunnableTest { public static void main(String[] args) { //线程0 奇数 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i <= 35; i++) { if (i % 2 != 0) { System.out.println(Thread.currentThread().getName() + "奇数" + i); } } } }).start(); //线程1 偶数 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i <= 20; i++) { if (i % 2 == 0){ System.out.println(Thread.currentThread().getName() + "偶数" + i); } } } }).start(); } }3.1实现Callable接口需要实现Callable接口并且重写call方法 优点是可以获取到线程的结果 缺点是分线程在执行中。当前线程会受到阻塞 效率低public class CallableTest implements Callable{ @Override public Object call() throws Exception { System.out.println("go go go!"); Thread.sleep(1000); System.out.println("come on!"); int a = 10; int b = 20; return a+b; } public static void main(String[] args) { //实例化当前类 CallableTest c = new CallableTest(); //实例化FutureTask类将当前类对象传递过去 FutureTask<Integer> f = new FutureTask<>(c); //实例化Thread类 Thread t = new Thread(f); //这里启动线程只会输出两句话 t.start(); Integer integer = null; try { //获取线程返回值 integer = f.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println("线程执行结果" + integer); } }Thread和Runnable的区别Thread是一个类，而Runnable是一个接口。因此，如果想要创建一个新的线程，可以继承Thread类并重写run()方法，也可以实现Runnable接口并实现run()方法。继承Thread类会限制了进一步继承其他类的能力，而实现Runnable接口则没有这个限制，可以继续继承其他类或实现其他接口。使用Runnable接口实现的多线程应用程序更加灵活，因为可以通过传递Runnable对象给Thread构造函数来创建线程，从而实现代码的分离和重用。实现Runnable接口还可以避免单继承的限制，因为Java不支持多重继承，但是可以同时实现多个接口。在运行效率方面，使用Runnable接口实现多线程时，多个线程可以共享同一个Runnable实例，而继承Thread类时每个线程创建一个独立的线程对象，因此Runnable通常比Thread更高效。Thread类的常用方法1.1Thread类中的构造器属性说明Thread()创建一个新的线程对象Thread(Runnable target)创建一个新的线程对象，并指定该线程要执行的任务Thread(String name)创建一个新的线程对象，并指定该线程的名称Thread(Runnable target, String name)创建一个新的线程对象，并指定该线程要执行的任务和名称Threadpublic class MyThread extends Thread{ public static void main(String[] args) { //Thread()：创建一个新的线程对象 new Thread(){ @Override public void run() { //获取线程名 System.out.println(this.getName() + " HelloWorld!"); } }.start(); //Thread(String name)：创建一个新的线程对象，并指定该线程的名称 new Thread("我是分线程:"){ @Override public void run() { //在运行的时候会提示 我是分线程: HelloWorld! System.out.println(this.getName() + " HelloWorld!"); } }.start(); } }Runnablepublic class MyRunnable implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName() + " 阿乐的小屋"); } public static void main(String[] args) { //Thread(Runnable target) 构造方法用于创建一个新的 Thread 对象，并指定该线程要执行的任务 MyRunnable m = new MyRunnable(); Thread thread = new Thread(m); thread.start(); //Thread(Runnable target, String name) 构造方法用于创建一个新的 Thread 对象，并指定该线程要执行的任务和线程的名称 Thread t = new Thread(m,"分线程"); t.start(); } }1.2Thread中的常用方法属性说明start()启动线程并调用线程的runThread.sleep(long millis)使当前线程暂停执行指定的时间（以毫秒为单位）getName()获取该线程的名称setName()设置该线程的名称Thread.yield()提示调度程序可以将CPU时间分配给其他线程。(满足一定条件可能会分给其他线程执行)join()等待该线程完成执行，并阻塞当前线程直到它完成为止isAlive()检查该线程是否处于活动状态getPriority()获取该线程的优先级setPriority()设置该线程的优先级seDaemon()守护线程Thread.MAX_PRIORITY（10）最高优先级Thread.MIN_PRIORITY（1）最低优先级Thread.NORM_PRIORITY（5）普通优先级 默认main具有普通优先级Thread.sleep线程暂停多少秒执行（运行状态）getName()获取线程的名称setPriority()设置线程的优先级getPriority()获取线程的优先级public class MyThread { public static void main(String[] args) { //线程一的优先级为10 new one("线程一",10).start(); //线程二的优先级为一 new one("线程二",1).start(); } } class one extends Thread{ public one() { } public one(String name,int pro){ //调用父类构造方法 super(name); //设置优先级 this.setPriority(pro); } @Override public void run() { for (int i = 0; i <= 10; i++) { try { //线程延迟1秒执行 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //获取线程的名称和优先级打印到控制台 System.out.println("我是" + getName() + "我的优先级是:" + getPriority() + "\t" + i); } } }Thread.yield()调用这个方法可以将cpu给其他线程public class MyThread { public static void main(String[] args) { new one("线程一:",5).start(); new two("线程二:",5).start(); } } class one extends Thread{ public one(String name,int pro) { super(name); this.setPriority(pro); } @Override public void run() { for (int i = 2; i <= 100; i+=2) { //如果i模2是偶数 那么cpu的执行权限就给线程二 if (i %2 == 0) { Thread.yield(); System.out.println("执行权先给线程二"); } System.out.println(getName() + i); } } } class two extends Thread{ public two(String name,int pro) { super(name); this.setPriority(pro); } @Override public void run() { for (int i = 0; i <= 100; i++) { System.out.println(getName() + i); } } }join() 等待该线程完成执行，并阻塞当前线程直到它完成为止（阻塞状态）public class MyThread02 { public static void main(String[] args) { Thread001 t = new Thread001(); t.setName("分线程"); t.start(); //主线程main 当i=10的时候会将cpu的权限给分线程 main线程会进入阻塞状态 当分线程运行完了以后才开始跑主线程 for (int i = 0; i < 50; i++) { if (i == 10) { try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("main --->" + i); } } } class Thread001 extends Thread{ @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println(getName() + i); } } }守护线程守护线程：比如一个软件打开以后会有一个线程专门来写入log日志 会持续10分钟写入一次 当软件关闭以后 那么这个线程也会跟随软件关闭public class MyThread03 { public static void main(String[] args) { Thread003 t = new Thread003(); Thread0031 t1 = new Thread0031(); t1.start(); //将Thread003实例化的对象设置为守护线程 当t线程运行结束后 守护线程会跟随t线程死亡 t.setDaemon(true); t.start(); } } class Thread0031 extends Thread{ @Override public void run() { for (int i = 0; i <= 10; i++) { System.out.println("执行" + i + "次"); } } } class Thread003 extends Thread{ public int i = 0; @Override public void run() { while (true) { System.out.println("我在向本地写入" + (i++) + "日志"); } } }多线程的生命周期​ Thread 类的生命周期可以分为 5 个阶段：新建状态（New）：当我们创建一个 Thread 对象时，线程处于新建状态。此时，它并没有开始执行，只是一个刚刚创建的对象。就绪状态（Runnable）:当调用 start() 方法启动线程后，线程进入了就绪状态。此时，线程已经准备好运行，等待系统分配 CPU 时间片来执行它的任务。在多线程环境中，有多个线程处于就绪状态，等待 CPU 的调度。运行状态（Running）:当线程被系统分配到 CPU 时间片并开始执行任务时，它就进入了运行状态。此时，线程正在执行其 run() 方法中的代码。阻塞状态（Blocked）:在线程执行过程中，可能会遇到一些耗时的操作（例如等待输入输出、获取锁等），这时线程就会进入阻塞状态。在这种状态下，线程暂停执行，直到满足某些条件才能继续执行。当条件满足时，线程将从阻塞状态转换为就绪状态，等待系统重新分配 CPU 时间片。死亡状态（Terminated）:当线程执行完其 run() 方法中的所有代码后，或者由于异常而意外终止时，线程就进入了终止状态。此时，线程已经完成了自己的任务，不再占用任何资源。线程安全问题以下示例中one类实现了Runnable接口并模拟了多个线程共享访问a资源。会导致数据不一致问题，线程会不安全。public class RunnableTest{ public static void main(String[] args) { one o = new one(); Thread t = new Thread(o,"线程一"); Thread t2 = new Thread(o,"线程二"); Thread t3 = new Thread(o,"线程三"); t2.start(); t.start(); t3.start(); } } class one implements Runnable{ private int a = 100; @Override public void run() { while (true) { if (a > 0) { System.out.println(Thread.currentThread().getName() + "取票:" + a--); } } } }synchronized同步代码块同步代码块：synchronized(this){方法体} （synchronized括号后的数据必须是多线程共享的数据，才能达到多线程排队）public class RunnableTest{ public static void main(String[] args) { one o = new one(); //模拟两个线程并发访问 会导致数据不安全 Thread t = new Thread(o,"线程一"); Thread t2 = new Thread(o,"线程二"); Thread t3 = new Thread(o,"线程三"); t.start(); t2.start(); t3.start(); } } class one implements Runnable{ private int a = 100; //lock是一个对象锁，只有获取了该锁的线程才能进入同步代码块执行，其他线程需要等待锁被释放后才能进入。 //Object lock = new Object(); @Override public void run() { while (true) { //可以使用类名.class当对象锁 synchronized (one.class) { if (a > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "取票:" + a--); } else { break; } } } } }synchronized同步方法创建建了一个实现了Runnable接口的类one，并在main方法中创建了三个线程分别用于调用one类中的run方法。使用while循环来保证线程一直运行。show方法被声明为synchronized，表示该方法只能同时由一个线程执行。在show方法中，使用while循环来判断是否还有票可取，如果有，则当前线程取票并打印信息，否则将b置为false表示没有票可取，结束循环。public class RunnableTest { public static void main(String[] args) { one o = new one(); Thread t = new Thread(o,"线程一"); Thread t1 = new Thread(o,"线程一"); Thread t2 = new Thread(o,"线程一"); t.start(); t1.start(); t2.start(); } } class one implements Runnable{ int ticket = 10; boolean b = true; @Override public void run() { while (b) { show(); } } //同步方法 public synchronized void show(){ while (b){ if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "取票:" + ticket--); }else { b = false; } } } }synchronized优点和缺点优点：可以避免多个线程同时对共享资源进行修改而导致的数据不一致问题，保证程序的正确性。缺点：会影响程序的性能，因为每当一个线程访问一个被synchronized修饰的方法或代码块时，都必须等待其他线程释放该锁才能执行。其次，如果不正确地使用synchronized，可能会导致死锁或竞争条件等问题银行有一个账户，有两个储户向同一个账户存3000元，每次存1000存3次，每次存完打印账户余额。public class AccountTest { public static void main(String[] args) { Account act = new Account(); Customer c = new Customer(act); Customer c1 = new Customer(act); Thread t = new Thread(c,"甲"); Thread t1 = new Thread(c1,"乙"); t.start(); t1.start(); } } //账户类 class Account{ private double balacnce; //余额 // public synchronized void deposit(double amt){ if (amt > 0) { balacnce+=amt; } System.out.println(Thread.currentThread().getName() + "存钱1000块:" + "余额为:" + balacnce); } } //用户类 class Customer implements Runnable{ Account account; public Customer(Account act) { this.account = act; } @Override public void run() { for (int i = 0; i < 3; i++) { account.deposit(1000); } } }多线程并发访问共享资源的情况，其中包括一个 Account 类和一个 Customer 类。在主函数中创建了两个 Customer 对象，它们共享同一个 Account 对象。Account 类有一个私有成员变量 balance 表示余额，并且有一个 synchronized 修饰的 deposit 方法，用于往账户里存钱。在 deposit 方法中先进行参数检查，如果存款金额大于零，则将余额增加该金额，并输出当前线程名字和余额。在 Customer 类中实现了 Runnable 接口，其构造函数接受一个 Account 对象作为参数，表示该 Customer 的操作会影响到这个账户。run 方法中使用一个 for 循环调用 Account 对象的 deposit 方法，每次存入 1000 元。由于 Customer 在不同的线程中运行，因此在多线程并发下，Account 的 deposit 方法需要被保护以避免竞态条件（race condition）的出现。在主函数中创建了两个 Thread 对象，分别传入不同的 Customer 对象和线程名字。然后启动两个线程，它们会同时运行，各自向同一个账户存入三次 1000 元。最终输出结果将展示线程对余额的交替更改，表明了多线程对共享资源的交互访问。Lock锁Lock是一种同步机制，用于协调多个线程对共享资源的访问加锁lock(); 解锁unlock() 一般在try catch finally中使用public class MyThread extends Thread{ static int a = 10; //定义lock锁 static Lock lock = new ReentrantLock(); @Override public void run() { while (true) { //加锁 lock.lock(); try { if (a <= 100) { System.out.println(this.getName() + a++); }else { break; } } catch (Exception e) { e.printStackTrace(); } finally { //释放锁 lock.unlock(); } } } } class ThreadTest{ public static void main(String[] args) { MyThread m = new MyThread(); m.setName("线程一"); m.start(); MyThread m1 = new MyThread(); m1.setName("线程二"); m1.start(); } }每个数字只能被一个线程输出一次，通过使用ReentrantLock实现线程安全的访问synchronzied和lock区别Synchronized是Java中的关键字，而Lock是Java提供的一个接口。Synchronized只能使用在方法或代码块中，而Lock可以在任何时候对共享资源进行锁定和释放。Synchronized在获取锁失败时会一直等待，而Lock可以通过tryLock()方法尝试获取锁，并返回是否获取成功。Lock可以实现公平锁，即先等待的线程先获取到锁，而Synchronized只能是非公平锁。Lock可以实现多个条件变量，即不同的线程可以针对不同的条件进行等待和唤醒，而Synchronized只能有一个条件队列。死锁两个或多个线程彼此持有对方需要的资源并相互等待，从而导致程序无法继续执行。以下示例中LockA 中先锁住了 obj1，然后尝试去锁住 obj2，而在 LockB 中先锁住了 obj2，然后尝试去锁住 obj1。如果这两个线程以相反的顺序同时运行，就会出现死锁状态public class LockTest { public static Object obj1 = new Object(); public static Object obj2 = new Object(); public static void main(String[] args) { LockA l = new LockA(); LockB l1 = new LockB(); Thread t = new Thread(l); Thread t1 = new Thread(l1); t.start(); t1.start(); } } class LockA implements Runnable { @Override public void run() { System.out.println("线程A开始执行 - - "); while (true) { synchronized (LockTest.obj1) { System.out.println("LockA 锁住了 obj1"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (LockTest.obj2) { System.out.println("LockA 锁住了obj2"); } } } } } class LockB implements Runnable{ @Override public void run() { System.out.println("线程B开始执行 - - "); while (true) { synchronized (LockTest.obj2) { System.out.println("LockA 锁住了 obj2"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (LockTest.obj1) { System.out.println("LockB 锁住了obj1"); } } } } }死锁产生的条件和处理措施Java中产生死锁的条件为：互斥条件：资源不能同时被多个线程持有。请求与保持条件：一个线程在持有某个资源的同时，又请求其他线程持有的资源。不剥夺条件：线程已经获得的资源只能由自己释放，不能被其他线程强制剥夺。循环等待条件：存在一个进程资源等待序列，使得每个进程都在等待下一个进程所持有的资源。要避免死锁，可以采取以下几种方式：避免互斥条件：尽量减少或避免对共享资源的独占性访问。避免请求与保持条件：一次性获取所有需要的资源，或者释放已经持有的资源再去获取新的资源。避免不剥夺条件：当线程持有某些资源时，如果需要获取其他资源而暂时无法获取，应该释放已经持有的资源。避免循环等待条件：通过给资源编号，要求所有线程按照编号顺序申请资源，或者通过设置一个超时时间，强制释放已经持有的资源线程通讯两个或多个线程之间通过共享内存或消息传递来实现协作的过程。其中一个线程需要等待另一个线程发出信号或响应某个事件，才能继续执行或完成任务Object类中的wait和notify方法属性说明wait()使当前线程进入等待状态并且会释放锁notify()唤醒正在等待的线程(如果有多个线程正在等待，则随机选择一个)notifyAll()唤醒所有等待中的线程Test类实现了Runnable接口，并且有一个私有成员变量number，用于表示当前计数器的值。run()方法是线程执行的主体，其中包含一个while循环，不断地执行以下操作：通过synchronized关键字来保证同步，只允许一个线程进入临界区，防止多个线程同时修改number。调用notify()方法唤醒等待中的线程，使其进入就绪状态。判断当前的number是否小于等于25，如果是，则打印当前线程的名称以及number的值，同时将number加1。通过Thread.sleep()方法暂停当前线程一段时间，模拟计数过程中的延迟。调用wait()方法使当前线程进入等待状态，并且释放锁对象，让其他线程可以进入临界区。如果number大于25，跳出循环，线程执行结束。利用synchronized、notify()和wait()方法实现了一个多线程的计数器，并且保证了线程间的同步和互斥，从而避免了数据竞争的问题。public class ThreadTest { public static void main(String[] args) { Test t = new Test(); Thread td = new Thread(t,"线程一"); Thread td1 = new Thread(t,"线程二"); td.start(); td1.start(); } } class Test implements Runnable{ private int number = 0; @Override public void run() { while (true){ synchronized (this) { //唤醒等待中的线程 notify(); if (number <= 25) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + number++); try { //让线程一进入等待状态 释放锁对象 线程二还可以进来 wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else { break; } } } } }生产者和消费者生产者和消费者共享一个缓冲区（LinkedList<Integer>对象），生产者向缓冲区添加物品，消费者从缓冲区取出物品。当缓冲区已满时，生产者线程会进入等待状态；当缓冲区为空时，消费者线程会进入等待状态。在添加或取出物品后，线程会唤醒所有在等待中的线程。同时，为了模拟添加或取出物品的时间，我们使用了Thread.sleep()方法来让线程睡眠一段时间。import java.awt.*; import java.util.LinkedList; public class TestDemo { public static void main(String[] args) { LinkedList<Integer> buffer = new LinkedList<>(); producer p = new producer(buffer); consumer c = new consumer(buffer); Thread t = new Thread(p); Thread t1 = new Thread(c); t.start(); t1.start(); } } //生产者 class producer implements Runnable{ private final LinkedList<Integer> buffer; private int count = 0; //计数器 producer(LinkedList<Integer> buffer) { this.buffer = buffer; } @Override public void run() { while (true){ synchronized (buffer){ while (buffer.size() == 10){ try { buffer.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //否则向缓存区添加物品 buffer.add(count); System.out.println("生产了" + count + "个产品"); count++; //唤醒所有等待中的线程 buffer.notifyAll(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } //消费者 class consumer implements Runnable{ private final LinkedList<Integer> buffer; consumer(LinkedList<Integer> buffer) { this.buffer = buffer; } @Override public void run() { while (true) { synchronized (buffer) { //如果缓存区为空就等待 while (buffer.size() == 0) { try { buffer.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //否则从缓存区取出物品 int value = buffer.removeFirst(); System.out.println("消费了" + value + "个产品"); buffer.notifyAll(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } }wait和sleep的区别wait属于Object类。它会使当前线程进入等待状态，并放弃对象锁。一般出现在同步代码块中或者同步方法中（线程之间的协调与通信，执行会进入阻塞）sleepThread类的静态方法，他可以让线程暂停执行指定的时间。时间过去之后线程会自动恢复执行（控制线程执行的时间，执行会进入阻塞）
IO流File类的常用方法File file = new file("路径的名称");属性说明getAbsoluteFile获取绝对路径getPath获取路径getName获取名称getParent获取上一层的目录length获取长度lastModified获取最后一次修改的时间list获取目录下的文件或文件名listFiles获取目录下的文件或文件名返回绝对路径renameTo剪切+粘贴示例：public class FileTest { public static void main(String[] args) { //在内存方面上 File file = new File("Hello.txt"); //相对路径 在当前的工程下创建Hello.txt File file1 = new File("D:\\Demo\\untitled\\src\\Day02\\File\\Hi.txt"); //绝对路径 System.out.println("绝对路径是：" + file.getAbsoluteFile()); System.out.println("路径是：" + file.getPath()); System.out.println("名称是：" + file.getName()); System.out.println("上一层的文件目录为：" + file.getParent()); //若无上层目录返回为NULL System.out.println("长度为：" + file.length()); System.out.println("获取最后一次修改的时间为：" + new Date(file.lastModified())); System.out.println(); System.out.println("绝对路径是：" + file1.getAbsoluteFile()); System.out.println("路径是：" + file1.getPath()); System.out.println("名称是：" + file1.getName()); System.out.println("上一层的文件目录为：" + file1.getParent()); //若无上层目录返回为NULL System.out.println("长度为：" + file1.length()); System.out.println("获取最后一次修改的时间为：" + file1.lastModified()); System.out.println(); show(); System.out.println(); add(); } public static void show(){ //输出这个目录下的文件和文件名 File file = new File("D:\\Program Files (x86)\\Tencent"); String[] list = file.list(); for (String s : list) { System.out.println("获取到的目录为：" + s); } System.out.println(); File[] files = file.listFiles(); for (File f : files) { System.out.println("获取到的绝对路径为：" + f); } } public static void add(){ File file = new File("Hello.txt"); File file1 = new File("D:\\Demo\\untitled\\src\\Day02\\File\\Hi.txt"); //file必须存在 file1 不可以存在 这个操作相当于是将Helo.txt 剪切 到D\\Demo\\untitled\\src\\Day02\\File目录 在粘贴为Hi.txt //linux表示：mv D/Demo/untitled/Hello.txt /D/Demo/untitled/src/Day02/File/hi.txt 移动+重命名 boolean b = file.renameTo(file1); System.out.println(b); } }运行结果：绝对路径是：D:\Demo\untitled\Hello.txt 路径是：Hello.txt 名称是：Hello.txt 上一层的文件目录为：null 长度为：5 获取最后一次修改的时间为：Wed Dec 21 16:13:40 CST 2022 绝对路径是：D:\Demo\untitled\src\Day02\File\Hi.txt 路径是：D:\Demo\untitled\src\Day02\File\Hi.txt 名称是：Hi.txt 上一层的文件目录为：D:\Demo\untitled\src\Day02\File 长度为：0 获取最后一次修改的时间为：0 获取到的目录为：TIM 获取到的目录为：WeChat 获取到的绝对路径为：D:\Program Files (x86)\Tencent\TIM 获取到的绝对路径为：D:\Program Files (x86)\Tencent\WeChat TrueFile类的判断创建删除判断：返回为boolean类型属性方法isDirectory判断是否是文件目录isFile判断是否是文件exists判断是否存在canRead判断是否可读canWrite判断是否可写isHidden判断是否隐藏public class FileTest02 { public static void main(String[] args) { //用图形化界面在这个目录创建一个likedx.txt文件 File file = new File("D:\\Demo\\untitled\\src\\Day02\\File\\likedx.txt"); System.out.println("判断是否是文件目录:" + file.isDirectory()); System.out.println("判断是否是文件:" + file.isFile()); System.out.println("判断是否存在：" + file.exists()); System.out.println("判断是否可读：" + file.canRead()); System.out.println("判断是否可写：" + file.canWrite()); System.out.println("判断是否隐藏：" + file.isHidden()); System.out.println(); File file1 = new File("D:\\Demo"); System.out.println("判断是否是文件目录:" + file1.isDirectory()); System.out.println("判断是否是文件:" + file1.isFile()); System.out.println("判断是否存在：" + file1.exists()); System.out.println("判断是否可读：" + file1.canRead()); System.out.println("判断是否可写：" + file1.canWrite()); System.out.println("判断是否隐藏：" + file1.isHidden()); } }创建和删除：返回为boolean类型属性说明createNewFile创建文件mkdir普通创建文件夹mkdirs覆盖创建文件夹delete删除文件或者文件夹不会经过回站public class FileTest04 { public static void main(String[] args) { //存在了就不创建 File file = new File("D:\\Demo\\untitled\\src\\Day03"); //不管存在不存在直接覆盖创建一个文件夹 File file1 = new File("D:\\Demo\\untitled\\src\\Day03\\Day04"); //linux下的mkdir //不存在就创建文件夹： mkdir D/Demo/untitled/src/Day03 //不存在就覆盖创建文件夹：mkdir -p D/Demo/untitled/src/Day03/11 System.out.println(file.mkdir()); System.out.println(file1.mkdirs()); //如果Day03下的Day04存在 那就删除新创建的src下的03和04 if (file1.exists()) { file1.delete(); System.out.println("删除成功"); } } }public class FileTest { public static void main(String[] args) { add(); show(); } //创建文件 public static void add() { File file = new File("D:\\likedx.txt"); try { //不存在就创建 存在就给个提示 if (file.createNewFile()){ System.out.println("文件已存在"); }else { System.out.println("文件已创建"); } } catch (IOException e) { e.printStackTrace(); //打印错误信息 } } //D盘中是否包含png文件 如果包含就打印出来 public static void show(){ File file = new File("D:\\"); File[] files = file.listFiles(); for (File f: files) { String name = f.getName(); if (name.endsWith("png")) { System.out.println(name); } } } }IO流框架分类字节输入流字节输出流字符输入流字符输出流抽象基类InputStreamOutputStreamReaderWriter访问文件FileInputStreamFileOutputStreamFileReaderFileWriter访问数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter访问管道PipedInputStreamPipedOutputStreamPipedReaderPipedWriter访问字符串 StringReaderStringWriter缓冲流BufferedInputStreamBufferedOuputStreamBufferedReaderBufferedWriter转换流 InputStreamReaderOutputStreamWriter对象流ObjectInputStreamObjectOutputStream FilterInputStreamFilterOutputStreamFilterReaderFilterWriter打印流 PrintStream PrintWriter推回输入流PushbackInputStream PushbackReader 特殊流DataInputStreamDataOutPutStream 什么是IO流?存储和读取数据的解决方案input（输入） output（输出） 数据就像水流一样进行传输IO流的作用用于读写数据（本地文件，网络）IO流按照流向可以分类哪两种流输出流：程序---->文件输入流：文件---->程序IO流按照操作文件的类型可以分类哪两种流字节流：可以操作所有类型的文件字符流：只能操作纯文本的文件什么是纯文本的文件txt md xml lrc json .....文件流字节流字节输出流FileOutputStream操作本地文件的字节输出流 可以把程序的数据写到本地文件中每次只能操作一个字节属性说明write（int b）一次写一个字节数据write（byte[] b）一次写一个字节数组数据write（byte []b, int off, int len）一次写一个字节数组的部分数据public class FileTest02 { public static void main(String[] args) throws IOException { //创建流 FileOutputStream fos = new FileOutputStream("a.txt"); //在原有基础上追加写入数据 //FileOutputStream fos = new FileOutputStream("a.txt",true); fos.write(97); //a //换行 String s = "\n"; byte[] bytes2 = s.getBytes(); fos.write(bytes2); byte[] bytes = {97,98,99,100,101}; //abcde //写入数据 一次性写进去byte数组 fos.write(bytes); //换行操作 String s1 = "\n"; byte[] bytes3 = s.getBytes(); fos.write(bytes3); byte[] bytes1 = {103,104,105,106,107,108}; //从0索引开始写进两个数据进去 fos.write(bytes1,0,2); //关闭流 fos.close(); } }字节输入流FileInputStream属性说明read（byte[] b）从输入流中读取一定数量的字节read(byte[] b,int off,int len)将输入流中最多 len 个数据字节读入 byte 数组操作本地文件的字节输入流，可以把本地文件中的数据读取到程序中来public class FileTest04 { public static void main(String[] args) throws IOException { //创建流 如果文件不存在直接报错 FileInputStream fis = new FileInputStream("src\\Demo3\\likedx.txt"); int read; //读取数据 //把读取到的结果赋给read 然后在打印出来 如果读到文件末尾会返回-1 while ((read = fis.read()) != -1) { System.out.print((char) read); } //关闭流 fis.close(); } }拷贝文件public class FileTest05 { public static void main(String[] args) throws IOException { //创建对象 FileInputStream fis = new FileInputStream("D:\\IO.png"); //创建拷贝对象 FileOutputStream fos = new FileOutputStream("src\\Demo3\\copy.png"); //拷贝文件 int len; while (((len = fis.read()) != -1)){ fos.write(len); } //关闭流 fos.close(); fis.close(); } }public class FileTest05 { public static void main(String[] args) throws IOException { //创建对象 FileInputStream fis = new FileInputStream("D:\\IO.png"); //创建拷贝对象 FileOutputStream fos = new FileOutputStream("src\\Demo3\\copy.png"); //拷贝文件 //创建一个byte类型的数组 byte[] bytes = new byte[1024]; int len; while (((len = fis.read(bytes)) != -1)){ //上面读取多少就写多少 fos.write(bytes,0,len); } //关闭流 fos.close(); fis.close(); } }字符流字符输入流FileReader字符流的底层就是字节流特点输入流：一次读一个字节，遇到中文时一次多多个字节输出流：底层会把数据按照指定的编码方式进行编码 变成字节在写入文件中对纯文本文件进行读写操作public class FileTest06 { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("1.txt");//北国风光 千里冰封 万里雪飘 int len; //read读取的内容转换为10进制 gbk一次读两个字节 utf8一次三个字节 如果想看到中文汉字 用char进行强制转换 while ((len = fr.read()) != -1) { System.out.print((char) len); } //关闭流 fr.close(); } }public class FileReaderTest { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("1.txt"); //北国风光 千里冰封 万里 //读取数据 这里使用的char数组而不是byte数组 char[] chars = new char[2]; int len; //read:读取数据 进行解码 强制转换为中文 while ((len = fr.read(chars))!= -1) { //把数组中的数据变成字符串进行打印 System.out.println(new String(chars,0,2)); //北国 风光 千 里冰 封 万里 } //关闭流 fr.close(); } }字符输入流原理底层创建字符输入流对象。并创建缓冲区（长度为8192字节数组）读取数据 判断缓冲区是否有数据可读缓冲区没有数据：就从文件获取数据装到缓冲区中，每次尽可能装满缓冲区 如果文件中没有数据了就返回-1缓冲区有数据：就从缓冲区中读取字符输出流FileWriter属性说明write（int c）写出一个字符write（String str）写出一个字符串write（String str, int off, int len）写出一个字符串的一部分(off = 下标开始 -->len 写出几个字符)write（char[] cbuf）写出一个字符数组write（char[] cbuf, int off, int len）写出字符数组的一部分(off = 下标开始 -->len 写出几个字符)public class FileWriterTest { public static void main(String[] args) throws IOException { //创建流 在文件中追加数据 FileWriter fw = new FileWriter("1.txt"); //写出一个字符 fw.write(97); String s = "\r\n"; fw.write(s); //a //写出一个字符串 String str = "我的博客"; fw.write(str); //我的博客 fw.write(s); //写出一个字符串的一部分 从下标1开始 写入bc fw.write("abcde",1,2); //bc fw.write(s); //写出一个字符数据 char[] chars = {'l','i','k','e','d','x','.','c','o','m'}; fw.write(chars); //likedx.com fw.write(s); //写出数组的一部分 fw.write(chars,0,2); //li //关闭流 fw.close(); } }字符输出流原理底层创建字符输出流对象。并创建缓冲区（长度为8192字节数组）写出数据 缓冲区装满了或者有数据会自动刷新后并写出数据到文件中flush 手动刷新 刷新完毕还可以继续写出数据如果关闭流，无法往文件中写出数据缓冲流字节流字节缓冲输入流BufferedInputStream属性说明read（byte[] b）从输入流中读取一定数量的字节read(byte[] b,int off,int len)将输入流中最多 len 个数据字节读入 byte 数组public class FileTest09 { public static void main(String[] args) throws IOException { //创建输入流进行读入数据 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("1.txt")); //创建输出流进行写出数据 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.txt")); //长度 int len; while ((len = bis.read())!= -1) { bos.write(len); } //关闭流 bos.close(); bis.close(); } }字节缓冲输入流BufferedOutputStream属性说明write（int b）一次写一个字节数据write（byte[] b）一次写一个字节数组数据write（byte []b, int off, int len）一次写一个字节数组的部分数据public class FileTest09 { public static void main(String[] args) throws IOException { //创建输入流进行读入数据 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("1.txt")); //创建输出流进行写出数据 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.txt")); //创建一个1024字节的数组 byte[] bytes = new byte[1024]; //长度 int len; //读入一次讲数据存储到数组当中 while ((len = bis.read(bytes))!=-1) { bos.write(bytes,0,len); } //关闭流 bos.close(); bis.close(); } }字节缓冲流原理字符流字符输入流BufferedReader字符缓冲输入流特有方法说明public String readLine()读取一行数据 如果没有数据可读了,会返回null 字符缓冲输出流特有方法说明public void newLine()写出回车到文件中public class FileTest10 { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new FileReader("1.txt")); //读取一行就换行 不会把回车读取到内存中 String str; while ((str = br.readLine()) != null) { System.out.println(str); } br.close(); } }字符输出流BufferedWriterpublic class FileTest11 { public static void main(String[] args) throws IOException { BufferedWriter bw = new BufferedWriter(new FileWriter("1.txt",true)); bw.write("我是一只小可爱"); //写出回车到文件中 bw.newLine(); bw.close(); } }缓冲流有哪几种？字节缓冲输入流：BufferedInputStream字节缓冲输出流：BufferedOutputStream字符缓冲输入流：BufferedReader字符缓冲输出流：BufferedWriter缓冲流为什么能提高性能?缓冲流自带长度为8192的缓冲区可以显著提高字节的读写性能对于字符流提升不明显，对于字节缓冲流而言关键是两个特有的方法字符缓冲流特有的是哪两个方法？字符缓冲输入流BufferedReader：readerLine()字符缓冲输出流BufferedWriter：newWriter()作业练习将一个文本中的内容在前面添加数字对每一行内容进行排序public class FileDemo10 { public static void main(String[] args) throws IOException { //123.txt用来存储没有排序的文本内容 456.txt为写出后的内容 BufferedReader br = new BufferedReader(new FileReader("D:\\123.txt")); BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\456.txt")); String data; //定义一个计数器 int count = 1; while ((data = br .readLine()) != null) { String s = count + "." + data; //写入的格式为1. 到文本结束 bw.write(s); //换行 bw.newLine(); //计数器+1 count++; } //原内容 //文本内容 //文本内容 //排序后 //1.文本内容 //2.文本内容 //3.文本内容 //关闭流 bw.close(); br.close(); } }将一个文本中的内容文章顺序进行恢复到一个新的文件中实现原理：使用字符缓冲输入流（BufferedReader）读取文本内容将文本的内容进行分割用数组存储 把分割好的内容存储到集合（TreeMap）当中 获取key和value使用字符缓冲输出流（BufferedWriter）进行写出数据。遍历集合中的key和value 用缓冲流写出到文本注意：在写出文本到文件中需要用到一个特有的方法newLine 写出一行自动进行回车public class FileTest11 { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new FileReader("1.txt")); String len; //创建一个集合用来添加数组中的数据 TreeMap<Integer,String> tm = new TreeMap<>(); //读取内容 创建一个数组将内容进行切割并存储到数组中 while ((len = br.readLine())!= null) { String [] arr = len.split("\\."); //将索引0和1添加到集合中 因为是Integer类型 所以这里0索引要强制转换类型 tm.put(Integer.parseInt(arr[0]),arr[1]); } //关闭流 br.close(); //写出文本到文件中 BufferedWriter bw = new BufferedWriter(new FileWriter("2.txt")); //对集合进行遍历 Set<Map.Entry<Integer, String>> entries = tm.entrySet(); for (Map.Entry<Integer, String> entry : entries) { //将遍历的key和value添加到缓冲流中进行写出数据到文本里面去 bw.write(entry.getKey() + "." + entry.getValue()); //每次写出一行进行换行操作 bw.newLine(); } bw.close(); } }用字符缓冲流实现一个验证程序次数的小程序当程序运行超过3次提示：软件免费试用3次。欢迎您注册会员后继续使用程序运行演示如下：第一次运行控制台输出：欢迎使用本软件，第1次使用免费第二次运行控制台输出：欢迎使用本软件，第2次使用免费第三次运行控制台输出：欢迎使用本软件，第3次使用免费第四次运行控制台输出：本软件只能免费使用3次 。欢迎您注册会员后继续使用public class FileTest12 { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new FileReader("1.txt")); //把文件的数字读取到内存当中 String line = br.readLine(); //字符串转换为包装类 int count = Integer.parseInt(line); count++; //判断 if (count<=3){ System.out.println("欢迎使用本软件，第" + count +"次使用免费"); }else if (count>=10) { System.out.println("检测到您运行次数较多限制使用"); }else { System.out.println("本软件免费运行3次.请您注册会员后进行使用"); } BufferedWriter bw = new BufferedWriter(new FileWriter("1.txt")); //将自增加之后的count写出到文件中 bw.write(count + ""); bw.close(); } }转换流将字节流转换为字符流或讲字符流转换为字节流支持设置字符编码、自动刷新缓冲区、解决半个中文字符等问题字符流（JDK11之前）InputStreamReader字符转换输入流OutputStreamWriter字符转换输出流public class FileTest { public static void main(String[] args) throws IOException { InputStreamReader isr = new InputStreamReader(new FileInputStream("1.txt"),Charset.forName("GBK")); //读取文本内容输出到控制台 int ch; while ((ch = isr.read())!=-1) { System.out.print((char) ch); } isr.close(); //将字符串写出到文本当中 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("1.txt"),Charset.forName("GBK")); osw.write("我爱学习,学习快乐"); osw.close(); } }字符流（JDK11之后）FileReader字符输入流public class FileTest02 { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("1.txt", Charset.forName("GBK")); int ch; //读取文本内容 while ((ch = fr.read()) != -1) { System.out.println((char) ch); } fr.close(); //利用转换流将字符串写出到文本中 FileWriter fw = new FileWriter("1.txt",Charset.forName("GBK")); fw.write("学习快乐 我爱学Java"); //关闭流 fw.close(); } }作业练习将GBK格式的文件转换为UTF-8格式的文件public class FileDemo11 { public static void main(String[] args) throws IOException { // file为源文件 file1为转换UTF-8写出的文件 File file = new File("D:\\123GBK.txt"); File file1 = new File("D:\\123UTF-8.txt"); //造流 FileInputStream fis = new FileInputStream(file); //转换输入流指定编码格式 InputStreamReader isr = new InputStreamReader(fis,"GBK"); //转换输出流指定编码格式UTF-8 FileOutputStream fos = new FileOutputStream(file1); OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8"); //读写过程 char [] chars = new char[1024]; int len; while ((len = isr.read(chars))!= -1) { osw.write(chars,0,len); } //关闭流 osw.close(); fos.close(); isr.close(); fis.close(); } }对象流（序列化流）可以把java中的对象写入到本地文件当中学生类Studentpublic class Student implements Serializable { private static final long serialVersionUID = 1L; private String name; private transient int age; private int id; public Student() { } public Student(String name, int age, int id) { this.name = name; this.age = age; this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getId() { return id; } public void setId(int id) { this.id = id; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", id=" + id + '}'; } }序列化类ObjectStreamDemo （字节输出流）public class ObjectStreamDemo { public static void main(String[] args) throws IOException { //创建一个文件输出流 FileOutputStream fts = new FileOutputStream("test.txt"); //创建一个对象流 ObjectOutputStream oot = new ObjectOutputStream(fts); Student stu = new Student("张三",18,1); Student stu2 = new Student("李四",21,2); //将对象存储在集合中 List list = new ArrayList(); list.add(stu); list.add(stu2); //对存储的集合数据进行序列化 oot.writeObject(list); //关闭流 oot.close(); fts.close(); } }反序列化类ObjectStreamDemo2（字节输入流）public class ObjectStreamDemo2 { public static void main(String[] args) throws IOException, ClassNotFoundException { FileInputStream fis = new FileInputStream("test.txt"); ObjectInputStream ois = new ObjectInputStream(fis); //反序列化进行读取 System.out.println(ois.readObject()); //关闭流 fis.close(); ois.close(); } }首先创建一个 Person 对象，并使用 ObjectOutputStream 将其写入到名为 person.ser 的文件中。然后，使用 ObjectInputStream 从同一文件中读取该对象，并将其转换为 Person 类型。public class ObjectStreamExample { public static void main(String[] args) { // 定义待写入的对象 Person person = new Person("Alice", 25); // 使用对象输出流将对象写入文件 try (ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("person.ser"))) { output.writeObject(person); System.out.println("成功写入对象：" + person); } catch (IOException e) { System.out.println("写入对象时发生异常：" + e.getMessage()); } // 使用对象输入流从文件中读取对象 try (ObjectInputStream input = new ObjectInputStream(new FileInputStream("person.ser"))) { Person p = (Person) input.readObject(); System.out.println("成功读取对象：" + p); } catch (ClassNotFoundException | IOException e) { System.out.println("读取对象时发生异常：" + e.getMessage()); } } } class Person implements Serializable { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } }总结：如果一个对象类的对象能被序列化，那么这个类必须实现一个接口Serializable序列化以后再次修改对象类，进行反序列化的时候会出现InvalidClassException异常需要在对象类中添加erialVersionUID（序列化、版本号）如果一个对象中的某个成员不想被序列化 给成员变量加transient关键字修饰。关键字标记成员变量不参与序列化过程打印流字节打印输出流PrinStream打印流一般是指PrinStream字节输出流,PrintWriter字符输出流两个类打印流只操作文件目的地，不能操作数据源特有的写出方法：可以实现，数据原样输出特有的写出方法：可以实现自动刷新，自动换行public class PrintTest { public static void main(String[] args) throws IOException { FileOutputStream fos = new FileOutputStream("1.txt"); PrintStream ps = new PrintStream(fos,true,Charset.forName("UTF-8")); ps.println("我爱学习,学习爱我"); //写出数据 自动换行 自动刷新 ps.println(97); ps.print(true); ps.println(); //换行 ps.printf("%s爱上了%s","阿珍","阿强"); //%s %s 占位符 //关闭流 ps.close(); fos.close(); } }字符打印输出流PrintWriterpublic class PrintTest02 { public static void main(String[] args) throws IOException{ PrintWriter pw = new PrintWriter(new FileWriter("1.txt")); pw.println("收藏从未停止,学习从未开始"); pw.print(123); //不换行 pw.close(); } }总结：打印流有字节打印流和字符打印流打印流不能操作数据源 只能操作目的地字节打印流：默认自动刷新。println自动换行字符打印流：自动刷新需要开启。特有的println自动换行作业练习：获取键盘输入 将输入的英文转换为大写public class FileDemo13 { public static void main(String[] args) throws IOException { //获取键盘输入 将字符流包装成缓冲流 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String s = null; while ((s = br.readLine())!= null) { System.out.println("----->" + s.toUpperCase()); } br.close(); } }
泛型Java 泛型（generics）是 JDK 5 新增的特性泛型的类型必须是类，不能是基本数据类型在JDK8当中创建泛型可以省略后面的 例如：GenericClass it = new GenericClass();GenericClass it = new GenericClass<>();java中泛型标记符：标记符说明EElement (在集合中使用，因为集合中存放的是元素)TType（Java 类）KKey（键）NNumber（数值类型）?表示不确定的 java 类型public class Test { public static void main(String[] args) { //使用泛型后 Map<Integer,String> map = new HashMap<>(); map.put(1,"张三"); map.put(2,"李四"); map.put(3,"王五"); map.put(4,"赵六"); Set<Map.Entry<Integer, String>> entries = map.entrySet(); for (Map.Entry<Integer,String> key : entries) { System.out.println(key.getKey() + "=" + key.getValue()); } } }自定义泛型类public class 类名 <泛型标记符>{ }//<T> 泛型的类型 public class Test <T>{ String name; int age; T test; public Test() { } public Test(String name, int age, T test) { this.name = name; this.age = age; this.test = test; } public T getTest() { return test; } public void setTest(T test) { this.test = test; } @Override public String toString() { return "Test{" + "name='" + name + '\'' + ", age=" + age + ", test=" + test + '}'; } }测试public class Test01 { public static void main(String[] args) { //如果定义了泛型类 实例化没有指明类型的泛型 则认为此泛型类型为Object类型 Test t = new Test(); t.setTest("likedx"); t.setTest(541884615); Object test = t.getTest(); System.out.println(test); //实例化指明泛型的类型为String Test<String> tt = new Test<>(); tt.setTest("https://likedx.com"); String test1 = tt.getTest(); System.out.println(test1); //下面会报错 因为指定了类型 //tt.setTest(123); //指明泛型的类型 Test<String> t2 = new Test<>("张三",18,"是大傻叉"); Test<Integer> t3 = new Test<>(); t3.setTest(18); } }P1的Test02不是泛型类 P2的Test02是泛型类public class Test02 extends Test<Integer>{}public class Test02<T> extends Test<T>{}总结自定义泛型类可能有多个参数 此时应将多个参数一起放在尖括号内 例如：<T1,T2,T3>泛型不同的引用不能相互赋值泛型类的构造器用默认的就可以如果泛型的类型不指定 泛型对应的类型均按照Object处理 但不等同于Object 要不用都别用泛型 要用就一路都用上静态方法中不能使用类的泛型异常类不能声明为泛型 public class MyException extends Exception{}父类有泛型子类可以选择保留泛型也可以选择指定泛型类型自定义泛型方法泛型方法和泛型类没有关系泛型方法可以声明为静态的public <泛型标识符> 返回值类型 方法名(泛型标识符[] 形参){ //方法体... }public class Test02 { public static void main(String[] args) { Integer [] i = new Integer[]{1,2,3,4,5,6}; String [] s = new String[]{"aa","bb","cc"}; Test02 t = new Test02(); t.printArr(s); } //方法的参数是一个数组，但是这个数组存放的数据类型不是具体的 而是一个泛型 public <E> void printArr(E[] arr){ for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } }泛型类使用情景假设数据库中有很多个表 对其中某一个表或者多个表进行增删改查处理 用泛型实现 user表结构idnameemailbirthphoto1张三zhang@126.com1998-02-15（NULL）2李四li@qq.com1995-10-12（NULL）3王五wang@163.com2001-11-03（NULL）Student表结构idnamescore1张三982李四653王五841.创建操作数据库的类public class DataBase<T> { //添加一条记录 public void add(T t){ } //删除一条记录 public boolean remove(T t){ return false; } //修改一条记录 public void update(int index,T t) {} //查询一条记录 public T getIndex(int index){ return null; } //查询多条记录 public List<T> getForList(int index) { return null; } }2.创建两个表public class Student { }public class user { }3.两个表继承数据库的类public class StudentDataBase extends DataBase<Student>{ }public class userDataBase extends DataBase<user>{ }4.对表进行处理public class DataBaseTest { public static void main(String[] args) { userDataBase u = new userDataBase(); u.add(new user()); u.getIndex(10); StudentDataBase student = new StudentDataBase(); student.add(new Student()); student.getIndex(15); student.remove(new Student()); } }泛型之通配符 <?>使用通配符后 可读 除了null可写 其他不行public class TestDemo { public static void main(String[] args) { List<Object> list = null; List<String> list1 = null; List<?> list2 = null; //类型转换异常 //list = list1; //list1 = list; list2 = list; list2 = list1; print(list); print(list1); } public static void print(List<?> list){ Iterator<?> iterator = list.iterator(); while (iterator.hasNext()) { Object obj = iterator.next(); System.out.println(obj); } } }限制条件的通配符? extends Person: 只允许泛型为Person的子类或者父类引用调用 也就是≤Person? super Person:只允许泛型比Person的子类或者父类引用调用 ≥Person创建一个继承关系的类public class Person { }public class Student extends Person{ }测试public class GenericTest { public static void main(String[] args) { List<? extends Person> list = null; List<? super Person> list1 = null; List<Person> l = null; List<Student> l1 = null; List<Object> l2 = null; list = l; list = l1; //list = l2; l2是超级类(Object) 必须得是小于等于Person才可以 list1 = l; //list1 = l1; list1不等于l1 因为l1是Student类继承了Person类 必须要比Person大于或者等于 list1 = l2; } }
集合到底是什么数组就是一个集合 集合实际上就是一个容器 可以来容纳其他类型的数据集合与数组的区别数组的长度是固定的 集合的长度是可变的数组中存储的是同一类型的元素，可以存储基本数据类型值。集合存储的都是对象。而且对象的类型可以不一致。主要内容Collection集合迭代器增强forList集合Set集合学习目标[ ] 能够使用迭代器对集合进行取元素[ ] 能够使用集合存储自定义类型[ ] 能够说出什么是集合[ ] 能够说出集合与数组的区别[ ] 能够说出ArrayList、LinkedList、Vector区别[ ] 够说出Set集合各种实现类的区别[ ] 能够说出Collection和Map集合的区别[ ] 说出Map集合的常用功能[ ] 能够遍历Map集合[ ] 能够说出各种Map集合实现类的区别[ ] 能够简单阐述HashMap的底层实现[ ] 能够查询和使用集合工具类的相关方法[ ] 能够说出Set与Map的关系集合框架Collection接口：单列集合 用来存储一个一个的对象List接口：有序的 可重复 支持索引ArrayList：作为List接口的主要实现类；线程不安全 效率高 底层使用数组存储LinkedList：底层使用双向链表存储 线程不安全 执行效率高Vector：作为List接口的古老实现类；线程安全的 效率低 底层使用数组存储Set接口：无序的 不可重复的数据HashSet：作为Set接口的主要实现类 线程不安全的 可以存储NULL值LinkedHashSet：有序 作为HashSet的子类 遍历内部数据时 可以按照添加的顺序遍历 频繁的遍历操作 效率高与HashSetTreeSet：添加数据要求是同一类型Map接口：双列集合 用来存储一对一对的数据（Key - value）HashMap：作为Map的主要实现类；线程不安全 效率高 存储Null的Key和valueLinkedHashMap：在HashMap原有的基础上 添加了一对儿指针，指向前一个和后一个元素TreeMap：按照添加的Key-value进行排序 实现排序遍历Hashtable：作为古老的实现类；线程安全 效率低 不能存储Null的Key和valueProperties：用来处理配置文件 Key和value都是String类型Collection 常用功能属性说明add添加元素对象到当前集合中addAll添加新集合中的所有元素对象到当前集合中remove删除第一个找到的与obj对象equals返回true的元素removeAll删除所有与coll集合中相同的元素isEmpty判断当前集合是否为空集合contains判断当前集合中是否存在一个与obj对象equals返回true的元素containsAll判断c集合中的元素是否在当前集合中都存在size查询当前集合中实际存储的元素个数toArray集合转换为数组Arrays.asList数组转换为集合hashCode查询当前对象的哈希值Collection集合基本使用package Collection01; import java.util.*; public class CollectionTest { public static void main(String[] args) { //1.添加元素 Collection coll = new ArrayList(); coll.add("张三"); coll.add(new String("Tom")); coll.add("李四"); Collection coll1 = new ArrayList(); coll1.add("likedx"); coll1.add("likedx1"); coll1.add("李哈哈"); coll1.add("李四"); //添加新集合中的所有对象到当前集合中 coll.addAll(coll1); System.out.println(coll); //2.删除元素 //coll.remove("张三"); //删除所有并且与coll集合中相同的元素 //coll.removeAll(coll1); System.out.println(coll); //3.判断元素 System.out.println("判断coll是否为空的集合" + coll.isEmpty()); System.out.println("判断张三是否存在Coll集合中" + coll.contains("张三")); System.out.println("判断coll1集合中的元素是否在当前集合中都存在" + coll.containsAll(coll1)); //4.查询 System.out.println("储存元素的个数为:" + coll.size()); //集合转换为数组 Object[] objects = coll.toArray(); //遍历数组 for (int i = 0; i < objects.length; i++) { System.out.println(objects[i]); } System.out.println("======================="); //数组转换为集合Arrays.asList(数组名).var List<Object> objects1 = Arrays.asList(objects); System.out.println(objects1); //查询当前对象的哈希值 System.out.println(coll.hashCode()); System.out.println("=============================="); Collection collection = new ArrayList(); Person p = new Person("张三",20); collection.add(p); collection.add(new Person("likedx",18)); collection.add("Tom"); //判断Tom是否在coll集合当中 System.out.println(coll.contains(new String("Tom"))); //判断时会调用obj对象所在类的equals 不重写Person类equals方法为false System.out.println(coll.contains(new Person("likedx",18))); } }class Person{ private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } /* public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && Objects.equals(name, person.name); }*/ }Iterator迭代器Iterator接口迭代：Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素，如果有，就把这个元素取出来，继续在判断，如果还有就再取出出来。一直把集合中的所有元素全部取出基本功能方法说明remove删除元素hasNext判断元素next输出元素package Collection01; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class IteratorTest { public static void main(String[] args) { Collection coll = new ArrayList(); coll.add("One"); coll.add("Two"); coll.add("Three"); coll.add("Four"); System.out.println("当前的数据是：" + coll); //创建迭代器对象 Iterator iterator = coll.iterator(); System.out.println(iterator.next()); System.out.println(iterator.next()); System.out.println(iterator.next()); System.out.println(iterator.next()); //输出的次数大于集合中的个数会导致无法获取到下一个元素 出现NoSuchElementException异常 //System.out.println(iterator.next()); //遍历集合 判断有没有元素 有就取出来 继续判断直到结束 while (iterator.hasNext()) { System.out.println(iterator.next()); } //删除迭代器中的所有元素 iterator.remove(); //再次访问会出错 出现NoSuchElementException异常 //System.out.println(iterator.next()); } }增强for增强for循环(也称for each循环)是JDK1.5以后出来的一个高级for循环，专门用来遍历数组和集合的。for(元素的数据类型 变量 : Collection集合or数组){ //遍历的元素 }遍历集合public class For_each { public static void main(String[] args) { Collection coll = new ArrayList(); coll.add("zhangsan"); coll.add("lisi"); coll.add("wangwu"); coll.add("zhaoliu"); //遍历集合 for (Object obj : coll) { System.out.println(obj); } } }遍历数组public class For_each { public static void main(String[] args) { //遍历数组 int [] arr = new int[]{1,2,3,4,5,6,7,8}; for (int i : arr) { System.out.println(i); } String [] str = new String[]{"zhangsan","lisi","wangwu","zhaoliu"}; for (String i : str) { System.out.println(i); } } }练习题public class For_each { public static void main(String[] args) { String [] arr = new String[]{"MM","MM","MM"}; /* for (int i = 0; i < arr.length; i++) { arr[i] = "GG"; } for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); //输出GG }*/ for (String str : arr) { str = "GG"; //此str非str str为局部变量 } for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]);//输出MM } } }List集合特点：作为List接口的主要实现类；线程不安全 效率高 底层使用数组存储元素有序 可重复List接口中常用方法方法说明add(Object obj)增remove(int index) / remove(Object ele)删set(int index,Object ele)改get(int index)查add(int index,Object ele)插size()长度Iterator迭代方式 增强for循环 普通的循环遍历indexof(Object o)字符串返回索引lastIndexOf(Object o)重复字符串返回索引contains(Object o)包含某字符串isEmpty是否为空addAll(Collection c)插入新集合removeAll(Collection c)删除新集合clear清空clone克隆toArray集合转数组Arrays.asList(集合的变量)数组转集合public class ArrayList_Test { public static void main(String[] args) { ArrayList arr = new ArrayList(); //增加数据 arr.add("zhangsan"); arr.add("lisi"); arr.add("wangwu"); //删除数据 Object remove = arr.remove(1); System.out.println(remove); //查看删除的数据 boolean lisi = arr.remove("zhangsan"); System.out.println(lisi); //返回布尔类型 判断是否删除成功 arr.add("zhaoliu"); arr.add("aa"); arr.add("bb"); //修改数据 Object likedx = arr.set(0, "likedx"); //将下标0是wangwu修改为likedx System.out.println(likedx); //查看修改前的数据 //查询数据 Object o = arr.get(0); System.out.println(o); System.out.println(arr); //集合转换数组 Object[] objects = arr.toArray(); //数组转换集合 List<Object> objects1 = Arrays.asList(objects); System.out.println("*************************"); //遍历集合方式一：普通遍历 /* for (int i = 0; i < arr.size(); i++) { System.out.println(arr.get(i)); }*/ //方式二：增强for /* for (Object obj : arr) { System.out.println(obj); }*/ //方式三：iterator迭代器 Iterator iterator = arr.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } }package Test1; public class Cat { private String name; public Cat() { } public Cat(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Cat{" + "name='" + name + '\'' + '}'; } }public class ArrayTest { public static void main(String[] args) { List list = new ArrayList(); //添加4只小猫 list.add(new Cat("英短猫")); list.add(new Cat("缅因猫")); list.add(new Cat("布偶猫")); list.add(new Cat("狸花猫")); //查询有几只小猫 System.out.println("小猫数量:" + list.size()); //4 //删除小猫 list.remove(0); System.out.println("小猫数量:" + list.size()); //3 //遍历小猫 for (int i = 0; i < list.size(); i++) { //向下转型 Cat cat = (Cat) list.get(i); System.out.print(cat.getName() + "\t"); //缅因猫 布偶猫 狸花猫 } System.out.println(""); //修改小猫 list.set(0,new Cat("蓝猫")); System.out.println("修改的小猫为:"); for (int i = 0; i < list.size(); i++) { //向下转型 Cat cat = (Cat) list.get(i); System.out.println(cat.getName()); //蓝猫 布偶猫 狸花猫 } } }ArrayList底层原理JDK1.7：ArrayList arr = new ArrayList(); //底层创建了长度是10的Object[]数组elementData如果elementData数组容量不够 那就扩容 默认情况下扩容为原来的1.5倍，同时需要将原有数组中的数据复制到新的数组中JDK1.8：ArrayList arr = new ArrayList(); //底层Object[]elementData初始化为0 没有创建长度arr.add(1); //第一次调用add时 底层才创建了长度10的数组，并将数据添加到elementData如果elementData数组容量不够 那就扩容 默认情况下扩容为原来的1.5倍，同时需要将原有数组中的数据复制到新的数组中LinkedList集合特点：底层使用双向链表存储线程不安全频繁的插入 删除 使用此类的效率比ArrayList高可以包含重复的元素方法说明getFirst获取头部信息getLast获取尾部信息addFirst指定头部添加信息addLast指定尾部添加信息remove默认删除第一个removeFirst删除头部信息removeLast删除尾部信息push(Object e)添加数据pop弹出数据public class Linkedlist_Test { public static void main(String[] args) { LinkedList list = new LinkedList(); //增删改查也比较重要 用法一样 //方式一: 入栈 list.add(1); list.add(2); list.add(3); System.out.println(list); //弹栈 System.out.println(list.pop()); System.out.println(list.pop()); System.out.println(list.pop()); //栈空了会 报错 NoSuchElementException //System.out.println(list.pop()); System.out.println("========================"); //方式二：入栈 先进后出 因在尾部last list.addLast(1); list.addLast(2); list.addLast(3); //弹栈 System.out.println(list.removeLast()); System.out.println(list.removeLast()); System.out.println(list.removeLast()); //栈空了会 报错 NoSuchElementException //System.out.println(list.removeLast()); } }Set集合特点：存储无序的 不可重复的数据Set的常用实现类有：HashSet、TreeSet、LinkedHashSet。Set集合支持的遍历方式和Collection集合一样：foreach和Iterator。向set中添加数据，所有的类一定要重写hashCode和equals实现去重HashSet无序性 存储的数据在底层数组中并非按照数组的索引顺序添加 而是根据数据的哈希值添加的Set 集合不允许包含相同的元素，如果试把两个相同的元素加入同一个 Set 集合中，则添加操作失败。作为Set主要实现类 线程不安全的 可以存储NULL值注意：HashSet 集合判断两个元素相等的标准：两个对象通过 hashCode() 方法比较相等，并且两个对象的 equals() 方法返回值也相等。因此，存储到HashSet的元素要重写hashCode和equals方法。package Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Objects; import java.util.Set; public class SetTest { public static void main(String[] args) { Set set = new HashSet(); set.add(122); set.add("AA"); set.add("BB"); set.add("CC"); set.add(456); set.add(123); set.add(new User1("张三",10)); set.add(new User1("张三",10)); Iterator iterator = set.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } } class User1{ private String name; private int age; public User1() { } public User1(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } /* @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User1 user1 = (User1) o; return age == user1.age && Objects.equals(name, user1.name); } @Override public int hashCode() { return Objects.hash(name, age); }*/ }LinkedHashSet特点：LinkedHashSet作为HashSet的子类 在添加数据的同时 每个数据还维护了两个引用（两个引用指前和后） 记录当前数据前一个数据和后一个数据频繁遍历 效率高 有序不可重复package Collection; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; public class SetTest02 { public static void main(String[] args) { Set set = new LinkedHashSet(); set.add("zhang"); set.add(123); set.add(4521); set.add("lisi"); set.add("wangwu"); set.add("wangwu"); System.out.println("元素的个数为：" + set.size()); for (Object obj : set) { System.out.println(obj); } } }TreeSet特点：不允许重复实现排序自然排序或定制排序如果使用的是自然排序，则通过调用实现的compareTo方法如果使用的是定制排序，则通过调用比较器的compare方法如何实现去重的？如果使用的是自然排序，则通过调用实现的compareTo方法如果使用的是定制排序，则通过调用比较器的compare方法如何排序？方式一：自然排序实现Comparable接口，并重写compareTo方法方式二：定制排序实现Comparator接口，重写compare方法自然排序根据姓名进行自然排序public class TreeSet_Test05 { public static void main(String[] args) { Set<Animal> set = new TreeSet<>(); set.add(new Animal("asd")); set.add(new Animal("vxc")); set.add(new Animal("bdfa")); set.add(new Animal("zwedsa")); Iterator<Animal> iterator = set.iterator(); while (iterator.hasNext()) { Animal next = iterator.next(); System.out.println(next); } } } class Animal implements Comparable<Animal>{ private String name; public Animal() { } public Animal(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Animal{" + "name='" + name + '\'' + '}'; } @Override public int compareTo(Animal o) { //升序 return this.name.compareTo(o.name); //降序 //return -this.name.compareTo(o.name); } }假设我们有一个类 cat，它包含两个属性：name 和 age。我们想要按照年龄进行排序：cat类需要实现Comparable并且重写compareTo方法public class TreeSet_Test04 { public static void main(String[] args) { Set<Cat> set = new TreeSet<>(); set.add(new Cat("狸花猫",1)); set.add(new Cat("蓝猫",3)); set.add(new Cat("博美",2)); Iterator<Cat> iterator = set.iterator(); while (iterator.hasNext()) { Cat next = iterator.next(); System.out.println(next); } } } class Cat implements Comparable<Cat>{ private String name; private int age; public Cat() { } public Cat(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Cat{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public int compareTo(Cat o) { //根据年龄从高到低进行排序 //return -Integer.compare(age,o.age); //根据年龄从低到高进行排序 return Integer.compare(this.age,o.age); } }定制排序根据字符串长度进行排序向set添加数据时其指定类需要实现Comparator接口并重写compare方法public class StringLengthComparator implements Comparator<String> { //实现Comparator并重写compare方法 @Override public int compare(String o1, String o2) { return Integer.compare(o1.length(),o2.length()); } }public class TreeSet_Test { public static void main(String[] args) { //在使用TreeSet时将StringLengthComparator传递给他的构造参数 //根据字符串的长度进行升序排序 Set set = new TreeSet(new StringLengthComparator()); set.add("a123"); set.add("a"); set.add("bcdfg"); set.add("12"); set.add("b123"); //只会按照插入的顺序进行排序 Iterator iterator = set.iterator(); while (iterator.hasNext()) { Object next = iterator.next(); System.out.println(next); //a a12 a123 bcdfg } } }假设我们有一个 Student 类，包含 name 和 score 两个属性。现在要按照学生分数从高到低排序：学生类需要重写tostring方法分数排序类需要实现implements接口并且实现compare方法public class TreeSet_Test02 { public static void main(String[] args) { //创建TreeSet的时候传入ScoreComparator构造方法 Set<Student> set = new TreeSet<>(new ScoreComparator()); set.add(new Student("张三",85)); set.add(new Student("李四",78)); set.add(new Student("王五",90)); set.add(new Student("赵六",88)); Iterator<Student> iterator = set.iterator(); while (iterator.hasNext()) { Student next = iterator.next(); System.out.println(next); //Student{name='李四', score=78} //Student{name='张三', score=85} //Student{name='赵六', score=88} //Student{name='王五', score=90} } } } class Student{ private String name; private int score; public Student() { } public Student(String name, int score) { this.name = name; this.score = score; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", score=" + score + '}'; } } //分数排序类 实现了Comparator接口 class ScoreComparator implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { //从高到低 //return -Integer.compare(o1.getScore(),o2.getScore()); //从低到高 return Integer.compare(o1.getScore(),o2.getScore()); } }组合排序假设我们有一个 Person 类，包含 name 和 age 两个属性。我们想要按照年龄升序排序，如果年龄相同，则按照名字升序排序年龄类实现Comparator接口重写compare方法对其进行排序public class TreeSet_Test03 { public static void main(String[] args) { Set<Person> set = new TreeSet<>(new AgeNameComparator()); set.add(new Person("阿乐",85)); set.add(new Person("李四",96)); set.add(new Person("王五",70)); set.add(new Person("张三",85)); Iterator<Person> iterator = set.iterator(); while (iterator.hasNext()) { Person next = iterator.next(); System.out.println(next); } } } class Person{ private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } } class AgeNameComparator implements Comparator<Person> { @Override public int compare(Person o1, Person o2) { int result = Integer.compare(o1.getAge(),o2.getAge()); if (result == 0){ return o1.getName().compareTo(o2.getName()); } return result; } }Map集合特点：Map和Collection没有继承关系Map集合是以Key和Value的方式存储数据 键值对map中的集合，元素是成对儿存在的(理解为夫妻)。每个元素由键与值两部分组成，通过键可以找对所对应的值Map常用方法属性说明put(Object key,Object value)添加数据putAll(Map m)添加对儿新集合clear()清空集合中的数据remove(Object key)通过key删除数据get(Object key)通过key获取valuecontainsKey(Object key)判断是否包含某个key(键)containsValue(Object value)判断是否包含某个value(值)isEmpty()判断是否为空keySet()获取map所有的key 也可以讲key转换为一个集合size()获取键值对的个数entrySet()将Map集合转换为Set集合values()获取所有的valueMap.Entry表示集合中的一个对象的实体key-valuepublic class Map_Test { public static void main(String[] args) { //创建Map集合对象 Map<Integer,String> map = new HashMap<>(); //向Map集合中添加对象 map.put(1,"zhangsan"); map.put(2,"lisi"); map.put(3,"wangwu"); map.put(4,"zhaoliu"); //通过key删除数据 map.remove(1); //判断是否包含某个key System.out.println(map.containsKey(2)); //判断是否包含某个value System.out.println(map.containsValue("wangwu")); //通过key获取value System.out.println(map.get(2)); //获取键值对的数量 System.out.println(map.size()); //获取所有的value Collection<String> values = map.values(); //遍历value for (String s : values) { System.out.println(s); } //清空集合 //map.clear(); } }Map集合的遍历基本：将map转换为set集合 通过node进行遍历 Set<Map.Entry<K, V>> set = map.entrySet(); 效率高通过set集合遍历keypublic class Map_Test { public static void main(String[] args) { Map map = new HashMap(); map.put(1,"zhangsan"); map.put(2,"lisi"); map.put(3,"wangwu"); map.put(4,"zhaoliu"); //方式一 迭代器遍历Key Set keys = map.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()){ System.out.println("Key的值是：" + iterator.next()); } System.out.println("==================="); //方式二 增强for Set keys1 = map.keySet(); for (Object obj : keys1) { System.out.println("Key的值是：" + obj); } System.out.println("==================="); } }通过key遍历valuepublic class Map_Test02 { public static void main(String[] args) { Map map = new HashMap(); map.put(1,"zhangsan"); map.put(2,"lisi"); map.put(3,"wangwu"); //遍历value Set keys = map.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()){ Object key = iterator.next(); System.out.println("value的值为：" + map.get(key)); } System.out.println("===================="); //增强for Set set1 = map.keySet(); for (Object value : set1) { System.out.println("value的值为：" + map.get(value)); } System.out.println("===================="); //方式二 Collection values = map.values(); for (Object value : values){ System.out.println("value的值为：" + value); } } }遍历集合例：public class Map_Test03 { public static void main(String[] args) { Map map = new HashMap(); map.put(1,"zhangsan"); map.put(2,"lisi"); map.put(3,"wangwu"); map.put(4,"zhaoliu"); //迭代器遍历集合 Set set = map.entrySet(); Iterator iterator = set.iterator(); while (iterator.hasNext()){ Object obj = iterator.next(); Map.Entry entry = (Map.Entry)obj; System.out.println(entry.getKey() + "=" + entry.getValue()); } System.out.println("================="); //增强for Set keys = map.entrySet(); for (Object obj : keys) { Map.Entry entry = (Map.Entry)obj; System.out.println(entry.getKey() + "=" + entry.getValue()); } } }例：public class Map_Test04 { //效率高 public static void main(String[] args) { Map<Integer,String> map = new HashMap<>(); map.put(1,"张三"); map.put(2,"王五"); map.put(3,"赵六"); //将map集合转换为set集合 通过迭代器方式进行遍历 //将map集合转换为set集合 Set<Map.Entry<Integer, String>> entries = map.entrySet(); //创建一个迭代器对象 Iterator<Map.Entry<Integer, String>> iterator = entries.iterator(); while (iterator.hasNext()){ //每一次取出node 通过node访问key和value Map.Entry<Integer, String> node = iterator.next(); System.out.println(node.getKey() + "=" + node.getValue()); } System.out.println("=============="); //增强for //将map集合转换为set集合 Set<Map.Entry<Integer, String>> entries1 = map.entrySet(); for (Map.Entry<Integer,String> key : entries1) { System.out.println(key.getKey() + "=" + key.getValue()); } } }HashMap底层实现原理JDK1.7：实例化后 底层创建了长度是16的一维数组Entry[]数组map.put(key1,value1)：调用key1的HachCode()计算哈希值，得到在Entry数组中的存放位置如果此位置上数据为空 则k1v1添加成功如果不为空 比较key1和已经存在的一个或者多个数据的哈希值如果key1的哈希值和已经存在数据的哈希值不相同 则添加成功如果key1的哈希值和已经存在的某一个数据的哈希值相同 调用key1所在类的equals(key2)方法比较如果equals()为false：则添加成功如果为true：使用value1替换value2扩容方式：扩容为原来容量的2倍，并将原有数据复制过来JDK1.8：实例化后 底层没有创建一个长度为16的一维数组Node[]数组首次调用put方法时 底层创建长度为16的数组JDK7底层结构：数组+链表JDK8底层结构：数组+链表+红黑树当数组的某一个索引位置上的元素以链表形式存在的数据个数>8 并且当前数组长度>64 此时此索引位置上的所有数据改为使用红黑树存储HashMap和TreeMap有什么区别HashMap的key可以为null、TreeMap不可以Properties属性类Properties是一个Map集合，Hashtable的子类 Properties的key和value都是String类型Properties被称为属性类对象 一般用来处理配置文件属性说明setProperty(String key,String value)添加元素getProperty(String key)通过key获取valuepublic class Properties_Test { //Properties:常用来处理配置文件。key和value都是String类型 public static void main(String[] args) { Properties pro = new Properties(); pro.setProperty("url","blogdx.com"); pro.setProperty("username","root"); pro.setProperty("password","123"); //通过key获取value String url = pro.getProperty("url"); System.out.println(url); System.out.println(pro.getProperty("username")); System.out.println(pro.getProperty("password")); } }Collections工具类Collections是一个操作Set、List和Map等集合的工具类Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作，还提供了对集合对象设置不可变、对集合对象实现同步控制等方法静态属性说明reverse翻转listshuffle随机排序sort自然排序swap(list,int i,intj)交换list集合中指定索引位置上的两个元素max获取最大值min获取最小值frequency(Object o)获取list集合中元素出现的次数synchronizedList将list集合转换为线程安全的public class Collections_Test { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add(123); list.add(123); list.add(23); list.add(-13); list.add(0); list.add(89); //翻转list中元素的顺序 Collections.reverse(list); //对list集合元素进行随机排序 Collections.shuffle(list); //对list进行自然排序 Collections.sort(list); //交换list集合中指定索引位置上的两个元素 Collections.swap(list,0,1); //获取最大值 //System.out.println(Collections.max(list)); //获取最小值 //System.out.println(Collections.min(list)); //获取list集合中123出现的次数 int frequency = Collections.frequency(list, 123); //synchronizedList:将list转换为线程安全的 List list1 = Collections.synchronizedList(list); System.out.println(list1); } }斗地主小游戏package Demo2; import java.util.*; public class DouDiZhu { public static void main(String[] args) { // 初始化一副牌 List<String> poker = initPoker(); // 洗牌 Collections.shuffle(poker); // 发牌 Map<Integer, List<String>> players = dealCards(poker); // 显示手牌 showCards(players); } /** * 初始化一副牌 */ private static List<String> initPoker() { List<String> poker = new ArrayList<>(); String[] colors = {"♠", "♥", "♣", "♦"}; String[] numbers = {"2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3"}; for (String color : colors) { for (String number : numbers) { poker.add(color + number); } } poker.add("小王"); poker.add("大王"); return poker; } /** * 发牌 */ private static Map<Integer, List<String>> dealCards(List<String> poker) { Map<Integer, List<String>> players = new HashMap<>(); // 底牌 List<String> bottom = new ArrayList<>(); // 发牌 for (int i = 0; i < poker.size(); i++) { String card = poker.get(i); if (i >= poker.size() - 3) { // 最后三张为底牌 bottom.add(card); } else { int playerNum = i % 3 + 1; // 玩家编号 List<String> playerCards = players.computeIfAbsent(playerNum, k -> new ArrayList<>()); playerCards.add(card); } } players.put(0, bottom); return players; } /** * 显示手牌 */ private static void showCards(Map<Integer, List<String>> players) { for (Map.Entry<Integer, List<String>> entry : players.entrySet()) { Integer key = entry.getKey(); List<String> value = entry.getValue(); System.out.print("玩家" + key + ": "); for (String s : value) { System.out.print(s + " "); } System.out.println(); } } }initPoker() 方法用于初始化一副扑克牌，包括52张普通牌和2张大小王，返回一个列表对象；dealCards() 方法接收一副已经初始化好的扑克牌，首先创建一个 HashMap 对象来存储三个玩家的手牌和三张底牌。之后通过循环遍历牌堆，将前51张牌均分给三个玩家，每个玩家17张牌。最后将最后三张牌作为底牌放入 bottom 列表中，并将 bottom 列表也放入 players 中，编号为0表示底牌；showCards() 方法用于展示每个玩家的手牌和三张底牌。遍历 players，对于每个键值对，输出玩家编号和对应的牌。在 main 方法中，首先调用 initPoker() 方法来初始化一副牌，然后使用 Collections.shuffle() 方法洗牌，再调用 dealCards() 方法发牌，最后使用 showCards() 方法展示每个玩家的手牌和三张底牌。
异常异常的概念异常指的是程序运行时发生的不正常事件；异常能够被程序处理，保证程序继续运行下去public class Demo { public static void main(String[] args) { int a = 10; int b = 0; //明知10/0 除不尽 非要运算导致算数异常 //java.lang.ArithmeticException System.out.println(a/b); } }开车--->撞车--->报警--->继续开车（由开车到撞车到报警解决问题）程序--->运行--->异常--->处理异常（程序运行出现异常、能够被处理还可以继续运行）开车--->撞车--->不报警--->协商无果--->打架（撞车处理不当导致事情无法解决）程序--->运行--->错误--->程序终止--->修改代码（程序运行出现错误、异常无法处理导致程序崩溃）异常结构图图中所示:Throwable是Java中所有错误（Error）和异常（Exception）的父类。在Java中，所有的异常和错误都必须继承自Throwable类。Error（错误）：JVM内部错误或系统错误，这类错误一般由JVM或操作系统引起，而程序本身一般无法处理和恢复。Exception： 程序运行过程中可能会出现的异常情况，包括可检查异常（Checked Exception）和不可检查异常（Unchecked Exception）两种类型。RuntimeException：子类都是运行时异常 其他都为编译时异常异常的语法public class Demo { public static void main(String[] args) { try{ //产生异常的代码段 }catch (异常类型 1){ //异常类型代码块 }finally { //最后执行的语句块 } } }try-catch-finally 块中使用 return 语句时，finally 块中的代码会在返回结果之前执行，但是通常情况下，return 语句仍然是最后执行的语句有一个特殊情况 finally 块可能不会被执行。那就是在程序运行的时候遇到了 System.exit() 方法调用。System.exit() 方法并不仅仅只在 finally 块中会导致程序退出。在任何地方调用这个方法都会使程序立即退出，包括在 main() 方法中调用 System.exit() 也会导致 JVM 关闭public class Demo { public static void main(String[] args) { //show(10,0); //单独执行只是说抛出了异常没有处理 try { show(10,0); } catch (Exception e) { e.printStackTrace(); //打印异常错误信息 } } public static void show(int a,int b){ if (b == 0) { //主动抛出异常 一般在方法内部使用但是并没有处理 throw new ArithmeticException("出现算数异常"); } } }public class Demo { public static void main(String[] args) { SimpleDateFormat sdf = new SimpleDateFormat(); Date date = new Date(); String str = sdf.format(date); System.out.println(str); //23-3-6 下午11:13 String s = "23-3-6 下午11:12"; //当出现异常直接对异常进行捕捉处理 try { Date parse = sdf.parse(s); } catch (ParseException e) { e.printStackTrace(); } System.out.println("s = " + s); //s = 23-3-6 下午11:12 } }自定义异常定义异常类--->继承Throwable类、继承Exception类或者RuntimeException类--->编写构造方法继承父类的实现--->实例化自定义对象--->使用throw抛出public class MyException extends Exception{ private int a; public MyException(int a) { this.a = a; } //打印异常信息 @Override public String toString() { return "MyException{" + "a=" + a + '}'; } }public class Demo2 { public static void main(String[] args) { try { show(11); } catch (MyException e) { throw new RuntimeException("MyException----->" + e); //Exception in thread "main" java.lang.RuntimeException: MyException----->MyException{a=11} // at Demo6.Demo2.main(Demo2.java:8) } } public static void show(int a) throws MyException { if (a > 10){ throw new MyException(a); } System.out.println(a); } }
Java日期类java.util.Date类封装当前的日期和时间 大部分方法都已经过时第一代日期类Date属性说明long getTime();获取当前时间，时间戳void setTime(long time);设置时间boolean after(Date when)测试此日期是否晚于指定日期。boolean before(Date when)测试此日期是否早于指定日期。public class DateTest2 { public static void main(String[] args) { //gettime 获取当前系统毫秒时间 (时间戳) Date d = new Date(); System.out.println(d.getTime()); //获取今天时间 此方法已过时 System.out.println(d.toLocaleString()); //获取昨天时间 Date d2 = new Date(d.getTime()-(60*60*24*1000)); System.out.println(d2.toLocaleString()); //比较今天是否晚于昨天 boolean after = d.after(d2); System.out.println(after); //比较今天是否早于昨天 boolean before = d.before(d2); System.out.println(before); } }SimpleDateFormat类属性说明format将一个Date格式转化为日期/时间字符串，此方法继承的父类DateFormat的方法parse从给定字符串的开始解析文本，以生成一个日期，此方法为继承的父类DateFormat的方法java.text.DateFormat类是日期/时间格式化子类的抽象类，它以与语言无关的方式格式化并解析日期或时间public class DateTest3 { public static void main(String[] args) throws ParseException { //创建日期 Date date = new Date(); //对日期进行格式化 //格式:yyyy-MM.dd HH:mm:ss SSS SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS"); System.out.println(sdf.format(date)); //字符串转换为时间 String str = "2001-10-17 10:17:15 888"; SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS"); Date parse = sdf1.parse(str); System.out.println(parse); } }第二代日期类CalendarDate类用于返回日期对象，不适合获取日历字段。设置和获取日历字段的方法，被JDK1.1推出的Calendar代替。属性说明getInstance()使用默认时区和语言环境获得一个日历，也可以通过getInstance(TimeZone time,Locale loc)或指定时区和语言环境的日历get(int field)获取指定日历字段set(int year,int month,int date,int hourOfDay,int minute,int second)设置字段YEAR、MONTH、DAY_OF_MONTH、HOUR、MINUTE和SECOND的值public class DateTest4 { public static void main(String[] args) { Calendar calendar = Calendar.getInstance(); int Year = calendar.get(Calendar.YEAR); //年 int Month = calendar.get(Calendar.MONTH)+1; //1月从0开始 月 int DayMonth = calendar.get(Calendar.DAY_OF_MONTH); //日 int Hour = calendar.get((Calendar.HOUR)); //时 int Minute = calendar.get(Calendar.MINUTE); //分 int Second = calendar.get(Calendar.SECOND); //秒 System.out.println(Year + "-" + Month + "-" + DayMonth + "\t" + Hour + ":" + Minute + ":" + Second); // 2023-3-4 5:36:52 } }public class DateTest5 { public static void main(String[] args) { Calendar calendar = Calendar.getInstance(); //获取今年的多少天 int DayYear = calendar.get(Calendar.DAY_OF_YEAR); //63 //获取这个月的多少天 int DayMonth = calendar.get(Calendar.DAY_OF_MONTH); //4 //获取这一周的第几天 自然周从周日开始默认是0 int DayWeek = calendar.get(Calendar.DAY_OF_WEEK); //7 System.out.println("今年已经过去了" + DayYear + "天 这个月已经过去了" + DayMonth + "天 这一周已经过去了" + DayWeek + "天"); } }第三代日期类JDK 1.0中包含了一个java.util.Date类JDK 1.1引入Calendar类之后被弃用了JDK1.8引用的java.time纠正了过去的缺陷LocalDateTime类属性说明LocalDate now()返回日期int getYear()返回年份int getMonthValue()返回月份int getDayOfMonth()返回日期int getHour()返回小时int getMinute()返回分钟int getSecond()返回秒public class DateTest6 { public static void main(String[] args) { //获取日期 LocalDateTime time = LocalDateTime.now(); System.out.println(time); // 2023-03-04T20:13:42.175 //年 int Year = time.getYear(); //月 int Month = time.getMonthValue(); //日 int dayOfMonth = time.getDayOfMonth(); //时 int hour = time.getHour(); //小时 int minute = time.getMinute(); //分钟 int second = time.getSecond(); System.out.println(Year + "年 " + Month + "月 " + dayOfMonth + "日 " + hour + "时 " + minute + "分 " + second + "秒 "); //2023年 3月 4日 20时 27分 14秒 } }DateTimeFormatter类DateTimeFormatter可以格式化LocalDate、LocalTime、LocalDateTime及Instant类属性说明static DateTimeFormatter ofPattern(String pattern)静态方法，返回一个DateTimeFormatter对象String format(TemporalAccessor t)格式化一个日期、时间，返回字符串TemporalAccessor parse(CharSequence text)将指定格式的字符序列解析为一个日期、时间public class DateTest7 { public static void main(String[] args) { //获取当前时间 LocalDateTime localDateTime = LocalDateTime.now(); System.out.println(localDateTime); //2023-03-04T20:31:57.261 //把日期对象解析成LocalDateTime对象 DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); //格式化日期 String format = dtf.format(localDateTime); System.out.println(format); //2023-03-04 08小时03分钟57秒 //字符串转换为日期 String sy = "2001-10-17 15:30:05"; TemporalAccessor parse = dtf.parse(sy); System.out.println(parse); //{},ISO resolved to 2001-10-17T15:30:05 } }
String类Java中的String类是一个不可变的字符串类型。属于引用类型，由于String对象是不可变的，所以每次对字符串进行操作时都会创建新的String对象构造方法属性说明public String()创建空白的字符串 不包含任何内容public String(String original)根据传入的字符串 创建字符串对象public String(char[] chs)根据字符数组 创建字符串对象public String(byte[] chs)根据字节数组 创建字符串对象public class StringDemo { public static void main(String[] args) { String s = "阿乐的小屋"; String s1 = new String("阿乐的小屋"); String s2 = new String(); //传递一个字符数组 将数组转换为字符串对象 char [] chs = {'a','b','c'}; chs[0] = 'z'; String s3 = new String(chs); //zbc //传递一个字节数组 将数组转换为字符串对象 byte [] bytes = {97,98,99,100,101}; bytes[0] = 90; String s4 = new String(bytes); //Zbcde } }String内存分析在使用双引号直接赋值的时候，系统会检查字符串在字符串常量池中是否存在 不存在就创建新的 存在了就复用 所以str = str1使用String构造方法new String创建两个对象 那么这两个对象在内存上地址是不相同的字符串的比较public class StringDemo02 { public static void main(String[] args) { String s = "blgodx.com"; String s1 = "Blgodx.com"; int a = 10; int b = 10; // == 如果是基本类型 比较的是数据值 如果是引用类型比较的地址值 System.out.println(s == s1); //true //比较字符串是否相同 System.out.println(s.equals(s1)); //true //忽略大小写进行比较 System.out.println(s.equalsIgnoreCase(s1)); //以下使用的==进行比较两个对象内存地址 由于Scanner是在栈内存中创建的 而str1是在堆内存中创建的 所以两个地址并不相同 Scanner scan = new Scanner(System.in); System.out.print("请输入一个字符串:"); String str = scan.next(); String str1 = "abc"; System.out.println(str == str1); } }String常用方法属性说明public boolean isEmpty()判断是否为空public boolean startsWith(String prefix)判断字符串以什么开头public boolean endsWith()判断字符串以什么结尾public String toUpperCase()将字符串小写转换为大写public String toLowerCase()将字符串大写转换为小写public char charAt()查找索引输出字符串public int indexOf(String str)查找字符串输出索引下标public int lastIndexOf()查找最后一个字符串的下标public String substring(int beginindex)从下标起始位置截取到结束public String substring(int beginindex,int beginendindex)从起始下标到结束下标截取字符串public String trim()去除字符串中的空格public String replace(char old Char,char newChar)将旧的字符替换为新的public class StringDemo { public static void main(String[] args) { String str = "blogdxm.com"; System.out.println(str.isEmpty()); //是否空 false System.out.println(str.startsWith("b")); //判断是否以b开头 true System.out.println(str.endsWith("m")); //判断是否以m结尾 true System.out.println(str.toUpperCase()); //将字符串转换为大写 System.out.println(str.toLowerCase()); // 将字符串转换为小写 System.out.println(str.charAt(0)); //打印0索引 b System.out.println(str.indexOf("m")); //查找m的下标 6 System.out.println(str.lastIndexOf("m")); //查找m的最后一个下标 10 System.out.println(str.substring(1)); //从下标1开始截取字符串并打印 logdxm.com System.out.println(str.substring(0,5)); //从下标0开始到5结束打印字符串 blogd System.out.println(str.replace('b', 's')); //将字符b替换为s } }作业练习获取键盘输入结尾是.jpg或者.png结尾 如果不是则打印错误信息 如果是则声称一个uuid 将uuid和获取到的文件类型拼接在一起输出public class StringDemo03 { public static void main(String[] args) { Scanner scan = new Scanner(System.in); System.out.print("请输入.jpg结尾或者.png结尾的名字:"); String str = scan.next(); if (!str.endsWith(".png") && !str.endsWith(".jpg")){ System.out.println("输入错误请重新输入"); }else { //获取最后一个. int find = str.lastIndexOf("."); UUID uuid = UUID.randomUUID(); //从.开始截取到最后 输入123.d.jpg 会直接截取.jpg String pic = str.substring(find); //打印内容 System.out.println(uuid + pic); } } }定义一个数组里面存放违规的字 获取用户输入 如果输入的内容包含数组中的内容将文本替换为*并且打印到控制台public class StringDemo04 { public static void main(String[] args) { Scanner scan = new Scanner(System.in); System.out.print("文明用语 人人有责:"); String input = scan.next(); char [] chars = {'死','傻','逼','草','操'}; for (char aChar : chars) { input = input.replace(aChar,'*'); } System.out.println(input); } }StringBufferStringBuffer()构造一个空的字符串缓冲区，并且初始化为 16个字符的容量。StringBuffer(int capacity);构造一个没有字符的字符串缓冲区和指定的初始容量。StringBuffer(String str);构造一个初始化为指定内容的字符串缓冲区。StringBuffer:可以给 可以不给 不给的情况下回自动扩容线程安全 线程安全 线程安全 适用多线程使用场景为频繁拼接 反转。public class StringDemo05 { public static void main(String[] args) { //定义一个空的字符缓冲区。底层创建16个字符的容量 StringBuffer sb = new StringBuffer(); //定义一个含有10个字符容量的字符缓冲区 StringBuffer sb1 = new StringBuffer(10); //capacity 容量大小 int capacity = sb.capacity(); System.out.println(capacity); //追加字符 sb.append(1); sb.append("阿乐的小屋"); sb.append(true); //这里的sb是StringBuffer类型 通过tostring方法转换为String类型 String str = sb.toString(); //转换为String类型调用substring 进行截取字符串 System.out.println(str.substring(1,6)); //反转字符串reverse StringBuffer reverse = sb.reverse(); System.out.println(reverse); } }StringBuffer和StringBuilder区别StringBuffer的方法都是同步的 StringBuilder方法是非同步的StringBuffer 线程安全 使用于多线程StringBuilder 线程不安全 适用于单线程 在单线程环境下比StringBuffer更快
Java常用类Object类Object是所有类的父类 所有类都默认继承了Object类属性说明hashCode返回该对象的哈希码值equals比较字符串是否相等toString返回对象的字符串表示形式getclass获取对象运行时类的class对象hashcodepublic class Demo { public static void main(String[] args) { String a = "张三"; String b = "李四"; System.out.println(a.hashCode()); //774889 System.out.println(b.hashCode()); //842061 System.out.println(a.hashCode() == b.hashCode()); //false } }equalspublic class Demo1 { public static void main(String[] args) { String a = "likedx.com"; String a1 = new String("likedx.com"); System.out.println(a.equals(a1)); //true String b = "likedx"; //这里用了字符串的拼接 b += ".com"; System.out.println(b.equals(a)); //true } }equals和==有什么区别equals用来比较字符串是否相同==用来比较字符串的内存地址toStringpublic class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } //这里可以用快捷键生成 alt+ins public String toString() { return "name = " + name + "\t" + "age = " + age; } }public class Demo2 { public static void main(String[] args) { Student s = new Student("张三",15); //这里调用的就是toString方法 System.out.println(s); //name = 张三 age = 15 } }getClasspublic class Demo3 { public static void main(String[] args) { Demo3 d = new Demo3(); System.out.println(d.getClass()); //class Demo3.Demo3 } }Math类属性说明Math.abs(a)取a的绝对值Math.sqrt(a)取a的平方根Math.cbrt(a)取a的立方根Math.max(a,b)取a、b之间的最大值Math.min(a,b)取a、b之间的最小值Math.pow(a,b)取a的b平方public class Demo3 { public static void main(String[] args) { //abs 绝对值 System.out.println("【绝对值】"+Math.abs(-10.3));//结果： 10.3 System.out.println("【最大值】"+Math.max(10,30));//结果： 30 System.out.println("【最小值】"+Math.min(10,30));//结果： 10 System.out.println("【正弦值】"+Math.sin(3.56));//结果： -0.4063057021444168 System.out.println("【对数值】"+Math.log(20));//结果： 2.995732273553991 } }Random类属性说明Random生成一个对象nextInt生成一个随机数，如果有参数则生成一个在0~参数之间的随机数nextBoolean生成一个随机的布尔值（true或false）nextDouble生成一个在0~1之间的小数nextFloat生成一个在0~1之间的小数nextLong生成一个随机的long型数public class Demo3 { public static void main(String[] args) { Random random = new Random(); //生成0-9之间的整数 int num = random.nextInt(10); System.out.println(num); int math = (int) (Math.random() * 10); System.out.println(math); System.out.println("=========="); //生成0-9之间的整数 用循环打印出来 for (int i = 0; i < 10; i++) { int n = random.nextInt(10); System.out.println(n); } } }包装类基本数据类型引用包装类型byteByteshortShortintIntegerlongLongfloatFloatdoubleDoublebooleanBooleancharCharacterpublic class User { private int age; private Integer pic; public static void main(String[] args) { User u = new User(); System.out.println(u.age); //0 System.out.println(u.pic); //null } }public class Demo4 { public static void main(String[] args) { int a = 10; // 自动装箱 int --- Integer Integer a1 = a; //自动拆箱 Integer --- int int i = a1.intValue(); System.out.println(a); //10 System.out.println(a1); //10 System.out.println(i); //10 } }类型互转public class Demo5 { public static void main(String[] args) { //String 转换为int String s = "100"; int i = Integer.parseInt(s); System.out.println("String转换int:" + "\t" + (i+1)); //String转换int: 101 //int转换为String int i1 = 100; String ss = String.valueOf(i1); System.out.println("int转换为String" + "\t" + ss+1); //int转换为String 1001 //int转换为Integer Integer integer = 100; System.out.println("int转换为Integer" + "\t" + integer); //int转换为Integer 100 //Integer转换为int Integer integer1 = 100; int i2 = integer1.intValue(); System.out.println("Integer转换为int" + "\t" + i2); //Integer转换为int 100 //String转换为Integer String s1 = "100"; Integer integer2 = Integer.valueOf(s1); System.out.println("String转换为Integer" + "\t" + integer2); //String转换为Integer 100 //Inreger转换为String Integer integer3 = 100; String s2 = String.valueOf(integer3); System.out.println("Inreger转换为String" + "\t" + s2+1); //Inreger转换为String 1001 } }