java基础(16-20)

一、day16–多线程

1、进程
  • 概念:就是正在运行的程序。也就是代表了程序锁占用的内存区域。

  • 特点:

    1. **独立性:**进程是系统中独立存在的实体,它可以拥有自己的独立的资源,每一个进程都拥有自己私有的地址空间。在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间。
    2. 动态性:进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。在进程中加入了时间的概念,进程具有自己的生命周期和各种不同的状态,这些概念在程序中都是不具备的。
    3. 并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。
2、线程
  • **概念:**线程(thread)是操作系统能够进行运算的调度的最小单位。它被包含在线程之中,是进行实际运作单位。一个进程可以开启多个线程。简而言之,一个程序运行后至少一个进程,一个进程里包含多个线程。

  • 进程和线程的关系

    一个操作系统中可以有多个进程,一个进程中可以有多个线程,每个进程有自己独立的内存,每个线程共享一个进程中的内存,每个线程又有自己独立的内存。

3、多线程的特性
  • 随机性:

img

  • 线程状态

imgimg

  1. 新建状态:
    使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

  2. 就绪状态:
    当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

  3. 运行状态:
    如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

  4. 阻塞状态:

    如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

    • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
    • 同步阻塞:线程在获取 synchronized同步锁失败(因为同步锁被其他线程占用)。
    • 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
  5. 死亡状态:
    一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

4、多线程创建1:继承Thread
  • **概述:**Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。Start()方法是一个native方法,它将通知底层操作系统,最终由操作系统启动一个新线程,操作系统将执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。

    创建对象
    Thread()
    分配新的 Thread 对象。
    Thread(Runnable target)
    分配新的 Thread 对象。
    Thread(Runnable target, String name)
    分配新的 Thread 对象。
    Thread(String name)
    分配新的 Thread 对象。
    常用方法
    long getId()
    返回该线程的标识符。
    String getName()
    返回该线程的名称。
    void run()
    如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任 何操作并返回。
    void setName(String name)
    改变线程名称,使之与参数 name 相同。
    static void sleep(long millis)
    在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),
    void start()
    使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
    void stop()
    终止线程。

  • 测试

//测试  Thread类
		public class Test2_Thread {
		    public static void main(String[] args) {
		        //1,创建对象
		        MyThread t = new MyThread();//新建状态
		        //2,开启线程
		//      t.run();
		 		t.setName("线程1");//修改线程名
		        t.start();//可运行状态
		        //3,run()和start()有什么区别?
		        //run()和start()都能执行任务,但是run()执行时就只是一个普通的方法调用没有多线程并发抢占的效果
		        //--模拟多线程--
		        MyThread t2 = new MyThread();
		//        t2.run();
		 		t.setName("线程2");//修改线程名
		        t2.start();
		        /*
		        4,多线程的执行结果具有随机性
		        Thread-0---0
		        Thread-1---0
		        Thread-0---1
		        Thread-1---1
		        Thread-0---2
		        Thread-1---2
		        Thread-1---3*/
		    }
		}
		//1,继承Thread类,实现多线程编程
		class MyThread extends Thread {
		    //2,在多线程编程里,把所有的业务放入--重写的run()里--generate--override methods..
		    //--需求:重复的打印10次线程名称
		    @Override
		    public void run() {//运行状态
		        for (int i = 0; i < 10; i++) {//fori
		            System.out.println(super.getName()+"---"+i);//getName()--获取线程名称
		        }
		    }//结束状态
		}
5、实现Runnable接口
  • 概述
    Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run的无参数方法。
  • 方法
    void run()
    使用实现接口 Runnable 的对象创建一个线程时,启动该线程将导致在独立执行的线程中调用对象的 run 方法。
  • 测试:
//测试 Runnable接口
		public class Test3_Runnable {
		    public static void main(String[] args) {
		        //1,创建目标对象
		        MyRunnable target = new MyRunnable();
		        //2,把要执行的目标 和Thread绑定关系
		        Thread t = new Thread(target);
		        t.setName("猪猪侠");
		        //3,启动线程
		        t.start();

		        //4,模拟多线程
		        Thread t2 = new Thread(target);
		        t2.setName("蜘蛛侠");
		        t2.start();
		/*      5,多线程的随机性
		        Thread-0===0
		        Thread-0===1
		        Thread-1===0
		        Thread-1===1
		        Thread-1===2
		        Thread-1===3
		        Thread-1===4
		        Thread-0===2
		        Thread-0===3
		*/
		    }
		}
		//1,实现Runnable接口,实现多线程编程
		class MyRunnable implements Runnable{
		    //2,如果有自己的业务,需要把业务 -- 放在重写的run()里
		    //--需求:打印10次线程名称
		    @Override
		    public void run() {
		        for (int i = 0; i < 20; i++) {
		            //Thread.currentThread()--获取正在执行任务的线程对象
		            //getName()--获取线程的名字
		            //3,Thread.currentThread().getName() -- 获取当前执行任务的线程对象的 名字
		            System.out.println( Thread.currentThread().getName() +"==="+i );
		        }
		    }
		}
  • 售票案例

集成thread

//测试  多线程售票
			public class Test4_Tickets {
			    public static void main(String[] args) {
			        //1,创建线程对象
			        MyTickets t1 = new MyTickets();
			        //2,启动线程卖票
			        t1.start();
			        //模拟多线程
			        MyTickets t2 = new MyTickets();
			        t2.start();
			        MyTickets t3 = new MyTickets();
			        t3.start();
			        MyTickets t4 = new MyTickets();
			        t4.start();
			        //3,问题1:四个人各卖了100张,总共卖了400张,不对,我们要求总共才卖100张.
			        //分析原因:
			        //程序中,一共new了4次.每个对象都拥有成员变量int tickets = 100 ;合着就拥有4份tickets,
			        //各卖各的,总共卖了400张.
			        //4,解决方案:tickets资源能在每个对象间共享--static
			    }
			}
			//1,继承Thread类实现多线程编程
			class MyTickets extends Thread{
			    //需求:设计4个售票窗口,总计售票100张。
			    //5,加static的原因是:把tickets变成共享资源,在全局是唯一的一份
			    static int tickets = 100 ;//定义变量,记录票数
			    //2,开始卖票业务 --写在run() --generate-override methods-ok
			    @Override
			    public void run() {
			        //3,一直卖票
			        while(true){//死循环,一定要配合出口!!!!
			            if(tickets > 0){
			             //6,放大招:如果多线程程序的数据,能够接受这次考验,那么,数据才是真的安全
			                //问题2:超卖,卖出了0 和 -1 的票
			                //问题3:重卖,把同一张票卖了好几个人
			                try {
			                    Thread.sleep(10);
			                } catch (InterruptedException e) {
			                    e.printStackTrace();
			                }
			                System.out.println( getName() +"====="+ tickets-- );
			            }else{
			                break ;
			            }
			        }
			    }
			}

实现runnable接口

//测试  多线程售票
			public class Test1_Tickets2 {
			    public static void main(String[] args) {
			        //1,创建目标对象
			        MyTickets2 target = new MyTickets2();
			        //2,启动线程
			        Thread t1 = new Thread(target);
			        t1.start();
			        //模拟多线程
			        Thread t2 = new Thread(target);
			        t2.start();
			        Thread t3 = new Thread(target);
			        t3.start();
			        Thread t4 = new Thread(target);
			        t4.start();
			    }
			}
			//实现接口,实现多线程编程
			//--需求:4个窗口,卖100张票
			class MyTickets2 implements Runnable {
			    int tickets = 100;//定义变量,记录票数
			    //2,开始卖票业务
			    @Override
			    public void run() {
			        //3,一直卖票
			        while (true) {//死循环,一定要配合出口!!!!
			            //重卖的原因:假设现在的 tickets = 93 , t1  t2  t3  t4四个人.进来 ,准备卖票
			            //超卖的原因:假设现在的 tickets = 1 , t1  t2  t3  t4四个人.进来 ,准备卖票
			            if (tickets > 0) {
			                //4,让程序休眠一下,来测试多线程编程中, 数据到底有没有隐患
			                //问题1:超卖,卖出了0 和 -1  和 -2 的票
			                //问题2:重卖,把同一张票卖了好几个人
			                try {
			                    //重卖的原因:假设现在的 tickets = 93 , t1  t2  t3  t4四个人.进来 ,睡了...
			                    //超卖的原因:假设现在的 tickets = 1 , t1  t2  t3  t4四个人.进来 ,睡了...
			                    Thread.sleep(10);
			                } catch (InterruptedException e) {
			                    e.printStackTrace();
			                }
			          //重卖的原因:
			          //假设t1先醒,醒了就开始卖票.由于现在tickets = 93,所以执行tickets--得93,并输出93,还没来得及变
			          //假设t2先醒,醒了就开始卖票. 认为现在tickets = 93,所以执行tickets--得93,并输出93,还没来得及变
			          //假设t4先醒,醒了就开始卖票. 认为现在tickets = 93,所以执行tickets--得93,并输出93,还没来得及变
			          //假设t3先醒,醒了就开始卖票. 认为现在tickets = 93,所以执行tickets--得93,并输出93,并自减变成92
			          // 超卖的原因::
			          //假设t1先醒,醒了就开始卖票.由于现在tickets = 1,所以执行tickets--得1,并输出1,变了,自减变成0了
			          //假设t2先醒,醒了就开始卖票. 现在tickets = 0, 所以执行tickets--得0,  并输出0,变了,自减变成-1了
			          //假设t4先醒,醒了就开始卖票. 现在tickets = -1,所以执行tickets--得-1,并输出-1,变了,自减变成-2了
			          //假设t3先醒,醒了就开始卖票. 现在tickets = -2,所以执行tickets--得-2,并输出-2,变了,自减变成-3了
			                System.out.println( Thread.currentThread().getName() + "=====" + tickets--);
			            } else {
			                break;
			            }
			        }
			    }
			}
6、比较
方式优点缺点
Thread编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。线程类已经继承了Thread类,所以不能再继承其他父类
Runnable线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。

二、day17–多线程2 + 注解

1、同步锁
  • 概述
    把共享资源,用锁锁起来.谁有钥匙,谁去用共享资源.没钥匙的就排队等待.
    实现了,同一时刻,没人抢的效果.
    专门用来解决多线程编程中的 数据安全隐患的 解决方案.
  • 同步和异步的区别
    –同步: 是指,同一时刻,有钥匙的是独占资源的,共享资源没有人抢,没有钥匙的人只能等着
    –异步: 是指,同一时刻,没人排队,大家抢着去用共享资源.
    –效率上来讲 : 异步 > 同步
    –安全上来讲 : 同步 > 异步
    –同步可以提高安全性,但是牺牲了效率/性能. 异步提高了效率,但是牺牲了安全.
  • 用法
    –通过synchronized 关键字实现同步锁
    –用在方法上 : synchronized public StringBuffer append(String str){}
    –用在代码块上 : synchronized( 锁对象 ){ 需要同步的代码; }
    –用在方法上,使用的锁对象是默认就是this.用在代码块上时,锁对象可以任意.
  • 改造
/改造 多线程售票
			public class Test2_Tickets2 {
			    public static void main(String[] args) {
			        //1,创建目标对象
			        MyTickets3 target = new MyTickets3();
			        //2,启动线程
			        Thread t1 = new Thread(target);
			        t1.start();
			        //模拟多线程
			        Thread t2 = new Thread(target);
			        t2.start();
			        Thread t3 = new Thread(target);
			        t3.start();
			        Thread t4 = new Thread(target);
			        t4.start();
			    }
			}

			//实现接口,实现多线程编程
			//--需求:4个窗口,卖100张票
			class MyTickets3 implements Runnable {
			    int tickets = 100;//定义变量,记录票数
			    Object obj = new Object();
			    String s = "jack";
			      @Override
			    //1,把方法加了synchronized关键字,实现了同步方法.同一时刻线程是独占资源的,没人抢,提高了数据的安全
			//    synchronized public void run() {//锁对象使用的是默认的this
			    public void run() {
			        while (true) {//死循环,一定要配合出口!!!!
			  //2,synchronized关键字还可以修饰代码块,实现同步代码块.考虑两点:锁的位置 + 锁的对象(可以任意,只要是同一个对象)
			   //同步代码块的语法  :  synchronized ( 锁对象  )  {   存在问题的代码      }
			          //  synchronized ( new Object() ){//不行,四个人用了4个锁,各锁各的
			            //synchronized (obj) {//可以,全局的唯一的成员变量
			            synchronized (this) {//可以,全局的唯一的成员变量
			//            synchronized (s) {//可以,全局的唯一的成员变量
			                if (tickets > 0) {
			                    try {
			                        Thread.sleep(10);
			                    } catch (InterruptedException e) {
			                        e.printStackTrace();
			                    }
			                    System.out.println(Thread.currentThread().getName() + "=====" + tickets--);
			                } else {
			                    break;
			                }
			            }
			        }
			    }
			}
//改造  多线程售票
			public class Test3_Tickets {
			    public static void main(String[] args) {
			        //1,创建线程对象
			        MyTickets4 t1 = new MyTickets4();
			        //2,启动线程卖票
			        t1.start();
			        //模拟多线程
			        MyTickets4 t2 = new MyTickets4();
			        t2.start();
			        MyTickets4 t3 = new MyTickets4();
			        t3.start();
			        MyTickets4 t4 = new MyTickets4();
			        t4.start();
			    }
			}
			class MyTickets4 extends Thread{
			    //0,加static的原因是:把tickets变成共享资源,在全局是唯一的一份
			    static int tickets = 100 ;//定义变量,记录票数
			    @Override
			    synchronized public void run() {
			 //2,同步的 普通方法,使用的是默认的锁对象是 this,根本锁不住静态资源
			 //3,同步的 静态方法,使用的是默认的锁对象是 类名.class
			//    public void run() {
			        while(true){//死循环,一定要配合出口!!!!
			      //1,同步代码块 需要自己指定锁对象(可以任意),但是,如果共享资源是静态的.锁对象只能是固定值类名.class
			//            synchronized (this){//this锁不住静态资源
			            synchronized (MyTickets4.class){
			                if(tickets > 0){
			                    try {
			                        Thread.sleep(10);
			                    } catch (InterruptedException e) {
			                        e.printStackTrace();
			                    }
			                    System.out.println( getName() +"====="+ tickets-- );
			                }else{
			                    break ;
			                }
			            }
			        }
			    }
			}
  • 总结

    –产生问题的原因:多线程里的共享资源是被抢占的.–是一个异步的,大家都去抢没人排队等待.
    –解决方案:加锁,保证了数据的安全,因为同一时刻的共享资源是被独占的没人抢.但是就降低了效率.
    –加锁实现了一个同步的效果,就是有钥匙的可以用,没钥匙的排队等待.虽然效率低但是安全.
    –怎么判断,你的程序中,数据有没有多线程编程时的安全隐患?
    –如果有多条语句操作了 共享资源 , 一定会有隐患 , 必须加锁

2、注解Annotation
  • 1,概念
    为了简化编程,用注解代替代码的编写. 注解的标志@ / @Override @Target @Test
  • 2,分类
    –jdk自带的注解一共5个,常用@Override
    –元注解一共有5个,常用的@Target @Retention
  • 3,@Target
    –用来描述注解的出现位置
    –值被维护在了ElementType.java里
    –类上 - TYPE
    –方法上 - METHOD
    –属性上 - FIELD
  • 4,@Retention
    –用来描述注解的生命周期
    –值被维护在了RetentionPolicy.java里
    –源文件中 -SOURCE
    –class文件中 -CLASS
    –运行时 -RUNTIME
  • 5,自定义注解

格式:

元注解
public @interface 注解名称{
	属性列表;
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
		//测试 自定义注解
		public class Test5_Annotation {
		    public static void main(String[] args) {

		    }
		}
		//一,自定义注解: @interface 注解名
		//@Target(ElementType.TYPE)//描述注解可以出现的位置--只能类上用
		@Target({ ElementType.TYPE, ElementType.METHOD })//描述注解可以出现的位置--多个位置上用
		@Retention(RetentionPolicy.SOURCE)//描述注解的生命周期
		public @interface Test{
		    //1,给注解添加功能--属性--语法特殊
		//    String local();
		    String local() default "" ;//3,给属性设置默认值,方便直接使用@Test
		    String value() default "" ; //5,特殊的属性value
		}


//二,使用注解: @Test
		//@Test(local = "class")//2,当Test注解添加了属性时,而且,属性又没设置默认值时,需要给属性赋值
		//@Test//4,由于属性设置好了默认值,用时就简单了
		@Test("123")//6,由于value属性比较特殊,可以简写.赋值时省略掉value=
		class Hello{
		//    @Test//位置非法,不能出现
		    String name;
		    @Test(local = "method",value = "???")//7,给多个属性赋值时,不能省略value=
		    public void show(){
		        System.out.println("show()");
		    }
		}

三、Day18–反射 + 内部类

1、反射
  • 概念:Reflection(反射) 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,也有称作“自省”。反射非常强大,它甚至能直接操作程序的私有属性。我们前面学习都有一个概念,private的只能类内部访问,外部是不行的,但这个规定被反射赤裸裸的打破了。

    反射就像一面镜子,它可以在运行时获取一个类的所有信息,可以获取到任何定义的信息(包括成员变量,成员方法,构造器等),并且可以操纵类的字段、方法、构造器等部分。

  • 作用: --拿到.class文件里的数据进行解析.
    –获取文件里的所有数据(公开的.私有的.构造.属性.方法)
    –并把所有数据封装在Class对象身上.

  • 获取Class对象
    Class.forName(“类的全路径”);
    类名.class
    对象.getClass();

  • 常用方法

获得包名、类名

clazz.getPackage().getName()//包名

clazz.getSimpleName()//类名

clazz.getName()//完整类名

!!成员变量定义信息

getFields()//获得所有公开的成员变量,包括继承的变量

getDeclaredFields()//获得本类定义的成员变量,包括私有,不包括继承的变量

getField(变量名)

getDeclaredField(变量名)

!!构造方法定义信息

getConstructor(参数类型列表)//获得公开的构造方法

getConstructors()//获得所有公开的构造方法

getDeclaredConstructors()//获得所有构造方法,包括私有

getDeclaredConstructor(int.class, String.class)

方法定义信息

getMethods()//获得所有可见的方法,包括继承的方法

getMethod(方法名,参数类型列表)

getDeclaredMethods()//获得本类定义的方法,包括私有,不包括继承的方法

getDeclaredMethod(方法名, int.class, String.class)

反射新建实例

c.newInstance();//执行无参构造

c.newInstance(6, “abc”);//执行有参构造

c.getConstructor(int.class, String.class); //执行含参构造,获取构造方法

反射调用成员变量

c.getDeclaredField(变量名); //获取变量

c.setAccessible(true); //使私有成员允许访问

f.set(实例, 值); //为指定实例的变量赋值,静态变量,第一参数给 null

f.get(实例); //访问指定实例的变量的值,静态变量,第一参数给 null

反射调用成员方法

获取方法

Method m = c.getDeclaredMethod(方法名, 参数类型列表);

m.setAccessible(true) ;//使私有方法允许被调用

m.invoke(实例, 参数数据) ;//让指定的实例来执行该方法

  • 反射的应用
//配合 测试 反射
		public class Student {
		    //构造方法-generate-constructor-多选ctrl/单选-ok
		    public Student() {
		    }
		    public Student(String name) {
		        this.name = name;
		    }
		    public Student(String name, int age) {
		        this.name = name;
		        this.age = age;
		    }
		    public Student(String name, int age, double score) {
		        this.name = name;
		        this.age = age;
		        this.score = score;
		    }

		    String  name ;
		    int age ;
		    double score ;

		    public void study(){
		        System.out.println("study()");
		    }
		    public void coding(){
		        System.out.println("coding()");
		    }

		}

测试:

import java.lang.reflect.Constructor;
		import java.lang.reflect.Field;
		import java.lang.reflect.Method;
		import java.util.Arrays;

		//测试 反射 获取Class对象
		public class Test2_ReflectStudent {
		    public static void main(String[] args) throws Exception {
		//        method();//获取Class对象
		//        method2();//获取构造方法
		//        method3();//获取成员方法
		//        method4();//获取成员变量
		        method5();//通过反射的技术创建对象
		    }
		    //创建对象
		    public static void method5() throws Exception {
		        //1,获取Class对象
		        Class<Student> clazz = Student.class ;
		        //2,调用实例化方法 --也要触发构造方法,而且触发的是无参构造
		        //newInstance()必须提供无参构造,否则[抛出异常:java.lang.InstantiationException: cn.tedu.reflect.Student
		        Student s = clazz.newInstance();
		        System.out.println("s = " + s);
		        //重写toString()前:s = cn.tedu.reflect.Student@1b6d3586
		       //重写toString()后:s = Student{name='null', age=0, score=0.0}

		        //怎么触发含参构造--触发public Student(String name)
		        //getConstructor(m);--m参数要 匹配构造方法里 参数的类型 的Class对象
		        Constructor<Student> c = clazz.getConstructor(String.class);//指定
		        //newInstance(x)--x是你创建对象时具体的参数
		        Student s2 = c.newInstance("jack");
		        System.out.println("s2 = " + s2);

		        //TODO 触发public Student(String name, int age)
		        Constructor<Student> c2 = clazz.getConstructor(String.class,int.class);
		        Student s3 = c2.newInstance("xiongda",20) ;
		        System.out.println("s3 = " + s3);
		    }
		    //获取成员变量
		    public static void method4() {
		        //1,获取Class对象
		        Class clazz = Student.class ;
		        //2,获取所有成员变量 -- 只能获取公开的变量
		        Field[] fs = clazz.getFields();
		        //3,遍历数组,得到每个变量f
		        for (Field f : fs) {
		            //4,获取变量名称
		            System.out.println(f.getName());
		            //5,获取变量类型
		            System.out.println(f.getType().getName() );
		        }
		    }
		    //获取成员方法
		    public static void method3() {
		        //1,获取Class对象
		        Class<Student> clazz = Student.class ;
		        //2,获取所有成员方法们 -- 包括自己的和父类的 的公开的方法们
		        Method[] ms = clazz.getMethods() ;
		        //3,遍历数组,得到每个方法m
		        for (Method m : ms) {
		            //4,获取方法名称
		            System.out.println(m.getName());
		            //5,获取方法的参数的类型
		            Class<?>[] cs = m.getParameterTypes();
		            System.out.println(Arrays.toString(cs));
		        }
		    }
		    //获取构造方法
		    public static void method2() {
		        //1,获取Class对象 --封装了.class文件里的所有数据
		        Class clazz = Student.class ;
		        //2,获取所有构造方法们  , 并存入数组 -- 只能反射 公开的 资源
		        Constructor[] cs = clazz.getConstructors();
		        //3,获取每个构造方法c
		        for (Constructor c : cs) {
		            //4,获取构造方法的名字
		            System.out.println( c.getName() );
		            //5,获取构造方法的参数类型 们
		            Class[] css = c.getParameterTypes();
		            System.out.println( Arrays.toString(css) );
		        }
		    }
		    //获取Class对象
		    public static void method() throws ClassNotFoundException {
		        Class c = Class.forName("cn.tedu.reflect.Student");//参数是类的全路径
		        Class c2 = Student.class ;
		//        Class c3 = new Student().getClass() ;//使用了父类Object提供的getClass()
		//        System.out.println("c3 = " + c3);
		//        System.out.println("c2 = " + c2);
		//        System.out.println("c = " + c);
		    }
		}
2、暴力反射

作用 : 不仅能获取到类的public资源,也可以获取到类里的private资源

API :
–普通反射
–getConstructor()
–getConstructors()
–getMethod()
–getMethods()
–getField()
–getFields()
–暴力反射
–getDeclaredConstructor()
–getDeclaredConstructors()
–getDeclaredMethod()
–getDeclaredMethods()
–getDeclaredField()
–getDeclaredFields()

//配置暴力反射
		public class Person {

		    public String name;
		    public int age;
		    private double score;

		    public void eat() {
		        System.out.println("eat()");
		    }
		    public void sleep(int a, String b) {
		        System.out.println("sleep()");
		    }
		    private void game(String x) {
		        System.out.println("game()");
		    }
		}
import java.lang.reflect.Field;
		import java.lang.reflect.Method;
		import java.util.Arrays;
		//测试 暴力反射:
		//条件 : 使用getDeclaredXxx()  + 开启私有可见的权限
		public class Test3_BaoliReflect {
		    public static void main(String[] args) throws Exception {
		//        method();//暴力反射成员方法们
		        method2();//暴力反射成员变量
		    }
		    //暴力反射成员变量们
		    public static void method2() throws Exception {
		        //1,获取Class对象
		        Class clazz = Person.class ;
		        //2,暴力反射
		        Field[] fs = clazz.getDeclaredFields();
		        //3,遍历数组,得到每个变量f
		        for (Field f : fs) {
		            //4,获取变量名称
		            System.out.println(f.getName());
		            //5,获取变量类型
		            System.out.println(f.getType().getName() );
		        }

		        //获取指定的变量
		        Field f = clazz.getDeclaredField("score") ;
		        //开启 访问权限,否则访问私有抛出异常:IllegalAccessException:
		        f.setAccessible(true);

		        Object obj = clazz.newInstance() ;
		        //设置值--set(1,2)--1是指对象名称--2是要设置的具体值
		        f.set(obj,99.9);
		        //获取值--get(1)--1是指对象名称
		        System.out.println( f.get(obj) );
		    }
		    //暴力反射成员方法们
		    public static void method() throws Exception {
		        //1,获取Class对象
		        Class<Person> clazz = Person.class ;
		        //2,暴力反射 -- getDeclaredMethods()
		        Method[] ms = clazz.getDeclaredMethods() ;
		        //3,遍历数组,得到每个方法m
		        for (Method m : ms) {
		            //4,获取方法名称
		            System.out.println(m.getName());
		            //5,获取方法的参数的类型
		            Class<?>[] cs = m.getParameterTypes();
		            System.out.println(Arrays.toString(cs));
		        }
		        //暴力反射 某个方法
		        //getDeclaredMethod(1,2)--获取指定的方法
		        //--1是指方法名--2是指方法需要的参数类型的Class对象
		        Method m = clazz.getDeclaredMethod("game",String.class) ;
		        //暴力反射:除了用对API,另外还需要开启访问权限
		        m.setAccessible(true);

		        Object obj = clazz.newInstance() ;
		        //执行方法--invoke(1,2)--1是指对象名称--2是方法需要传入的参数
		        m.invoke(obj,"tony") ;
		    }
		}
3、内部类
  • 概述
    如果一个类存在的意义就是为指定的另一个类,可以把这个类放入另一个类的内部。就是把类定义在类的内部的情况就可以形成内部类的形式。A类中又定义了B类,B类就是内部类。B类可以当做A类的一个成员看待。

  • 特点
    1、 内部类可以直接访问外部类中的成员,包括私有成员

2、 外部类要访问内部类的成员,必须要建立内部类的对象

3、 在成员位置的内部类是成员内部类

4、 在局部位置的内部类是局部内部类

  • 结构

    class A{//外部类
    			public void show(){
    				//局部内部类  -->  匿名内部类 !!!
    				class C{
    				}
    			}
    			//成员内部类 只为 外部类服务 -- 看做是外部类的一个特殊的成员
    			class B{//内部类
    			}
    		}
    
  • 入门案例

    //测试 内部类
    		public class Test1_Inner {
    		    public static void main(String[] args) {
    		        //TODO 调用内部类的资源 --创建内部类对象
    		        //语法:  外部类名.内部类名  变量名 = 外部类对象.内部类对象
    		        Outer.Inner in = new Outer().new Inner();
    		        in.in();
    		        System.out.println(in.age);
    		    }
    		}
    		class Outer{//外部类
    		    String name = "jack";
    		    public void out(){
    		        //3,外部类 访问内部类的 成员 -- 不可以直接用,需要创建内部类对象
    		        new Inner().in() ;
    		        System.out.println("out()");
    		    }
    		    //1,内部类--可以看做是外部类的一个特殊成员,和其他成员是同级关系
    		    class Inner{
    		        int age = 20 ;
    		        public void in(){
    		            //2,内部类 访问外部类 的成员 ?-- 可以
    		            System.out.println(name);
    		            System.out.println("in()");
    		        }
    		    }
    		}
    
4、匿名内部类

–通常存在 局部位置(方法里) ,通常要配合 匿名对象 一起使用
–测试接口

//测试 匿名内部类
			//总结
			//1,接口也可以直接new,但是要配合  匿名 内部类 ,完成抽象方法的重写 !!
			public class Test2_Inner {
			    public static void main(String[] args) {
			        //TODO 2,优化方案:直接new接口,要配合匿名内部类使用(在内部类里重写抽象方法)!!
			        new Inter(){
			            @Override
			            public void save() {
			                System.out.println("数据保存成功!");
			            }
			            @Override
			            public void delete(int id) {
			                System.out.println("数据删除成功!,id是:"+id);
			            }
			        }.save();//调用指定方法
			//        }.delete(5);//3,注意::::调用指定方法---匿名对象一次只执行一个任务,只能二选一

			        // 4,给匿名对象起个名字  !!!  -- 方便使用同一个对象干多个事情 !!!
			        Inter in = new Inter(){
			            @Override
			            public void save() {
			                System.out.println("数据保存成功!");
			            }
			            @Override
			            public void delete(int id) {
			                System.out.println("数据删除成功!,id是:"+id);
			            }
			        };
			        //5,使用有名字的同一个对象 ,干两件事
			        in.save();
			        in.delete(10);
			    }
			}
			interface Inter{
			    //简写形式
			    void save() ;
			    void delete(int id);
			}
			//TODO 1,可以优化 ...
			//class InterImpl implements Inter{
			//    @Override
			//    public void save() {
			//        System.out.println("数据保存成功!");
			//    }
			//    @Override
			//    public void delete(int id) {
			//        System.out.println("数据删除成功!,id是:"+id);
			//    }
			//}

–测试抽象类

//测试 匿名内部类
			//抽象类
			public class Test3_Inner {
			    public static void main(String[] args) {
			//5,优化继承结构 --抽象类也可以直接new,必须配合 匿名 内部类,完成抽象方法的重写!!
			        new Demo() {
			            //6,在匿名内部类  里  重写 抽象方法,对于普通方法 ,需要改的话也可以重写 !!
			            @Override
			            public void test() {
			                System.out.println("test()");
			            }
			        }.test() ;
			    }
			}
			//TODO 1, 抽象类 -- 可以有抽象方法也可以有普通方法
			abstract class Demo{
			    public void show(){
			        System.out.println("show()");
			    }
			    //2,抽象类里的方法,必须写全了,根本没有简写形式
			    abstract public void test() ;
			}
			3,子类继承了抽象类以后, 要么是重写所有 抽象方法,否则就是一个抽象类
			//class Demo2 extends  Demo{
			//    //4,子类强制要求重写的方法是 抽象方法,对于普通方法需要重写吗? -- 如果要改才重写普通方法
			//    @Override
			//    public void test() {
			//        System.out.println(test()");
			//    }
			//}

四、day19–socket

1、认识socket
  • 概述
    在网络间,完成数据的传输. 把数据抽象的在网络间传递.对于电脑来讲,都可以完成收和发的过程.
    socket通信,本质上就是把数据 抽象成 IO流的形式在网路中传输.

img

  • 工具类
    –ServerSocket
    –此类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。
    –创建对象
    ServerSocket(int port)
    创建绑定到特定端口的服务器套接字。
    –常用方法
    Socket accept()
    侦听并接受到此套接字的连接。
    void close()
    关闭此套接字。
  • Socket
    –此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
    –创建对象
    Socket(String host, int port)
    创建一个流套接字并将其连接到指定主机上的指定端口号。
  • 常用方法
    InputStream getInputStream()
    返回此套接字的输入流。
    OutputStream getOutputStream()
    返回此套接字的输出流。
2、案例

服务器端接收客户端发来的hello,并给客户端响应hello。

服务器端

说明其中,server端的accept()是阻塞的,客户端不连接,服务器不执行后面流程。

in.read()也是阻塞的,读不到就死等。

import java.io.IOException;
		import java.io.InputStream;
		import java.io.OutputStream;
		import java.net.ServerSocket;
		import java.net.Socket;
		import java.util.Scanner;

		//socket通信的服务器端
		public class Server {
		    public static void main(String[] args) throws IOException {
		        //1,开启服务器 -- 端口号0~65535
		        //在指定的8000端口号处,等待客户端的连接
		        ServerSocket server = new ServerSocket(8000);
		        System.out.println("服务器已开启...");

		        //2,接收客户端连接请求,并建立通信通道Socket
		        Socket socket = server.accept() ;
		        System.out.println("接收一个客户端的请求...");

		        //3,读取客户端发来的数据
		        InputStream in = socket.getInputStream();
		        //读到的数据默认是int,转成字符类型
		        for (int i = 0; i < 5; i++) {
		            char b = (char)in.read() ;
		            System.out.print(b);//同行展示
		        }

		        //4,服务器给客户端发送数据
		        OutputStream out = socket.getOutputStream();
		        //写出 动态的 数据
		        System.out.println("请输入想要发送给客户端的数据:");
		        String input = new Scanner(System.in).nextLine();
		        out.write(input.getBytes());//String->byte[]

		        out.flush();

		    }
		}

客户端

import java.io.IOException;
		import java.io.InputStream;
		import java.io.OutputStream;
		import java.net.Socket;
		import java.util.Scanner;

		//socket通信的客户端
		public class Client {
		    public static void main(String[] args) throws IOException {
		        //1,连接 定 ip地址 和 端口 的服务器
		        //实际工作中,要写服务器的真实ip -- 本机ip--127.0.0.1
		        Socket socket = new Socket("127.0.0.1",8000);
		        System.out.println("客户端与服务器连接成功...");

		        //2,给服务器写出数据
		        OutputStream out = socket.getOutputStream();
		        //写出 动态的 数据
		        System.out.println("请输入想要发送给服务器的数据:");
		        String input = new Scanner(System.in).nextLine();
		        out.write(input.getBytes());//String->byte[]

		        out.flush();

		        //3,读取服务器发来的数据
		        InputStream in = socket.getInputStream();
		        //读到的数据默认是int,转成字符类型
		        for (int i = 0; i < 5; i++) {
		            char b = (char)in.read() ;
		            System.out.print(b);//同行展示
		        }

		    }
		}

五、day20–综合

1、jdk的新特性

1,概述
每个版本的jdk软件都提供了一些新语法,有的语法不需要,有的语法很实用很重要.
2,重点语法
JDK 5
自动装箱拆箱
foreach
*可变长参数(Varargs)
泛型
JDK 7
switch 语句支持 String:switch语句块中允许以字符串作为分支条件
类型推断:创建泛型对象时应用类型推断
*try-with-resources:自动关闭流
*catch 多个类型异常:一个语句块中捕获多种异常
JDK 8(2014-3-18)
*Lambda 表达式 ? Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
3,测试 可变长 参数

	import java.util.Arrays;
	//测试  jdk的新特性
	// 1.5 --可变长 参数(Varargs)
	public class TestJDK {
	    public static void main(String[] args) {
	//        method();
	        method(5);
	        method(5,10);
	        method(5,10,20);
	        method(5,10,20,6);
	    }
	    //TODO 优化: 提供一个method() ,但是可以动态 匹配 N 个参数
	    //TODO jdk1.5提供了可变长度的参数 ,根部不限制参数的个数.几个都行.可以省略  ,语法就是 类型后面加...
	    public static void method(int a,int... b){
	        //TODO 可变参数,本质上 是个数组,可以没有也可以有多个
	        //TODO 细节:可变参数 必须是参数列表的最后一个,这时,会把第一个参数匹配给a,剩下的都给b
	//        System.out.println(a);//[I@1b6d3586
	        System.out.println( Arrays.toString(b) );
	    }
	//方法重载:在同一个类里的现象. 方法名相同,但是参数列表(类型/个数)不同的现象--因为灵活
	//    public static void method(int a){
	//        System.out.println(a+a);
	//    }
	//    public static void method(int a,int b){
	//        System.out.println(a+b);
	//    }
	//    public static void method(int a,int b,int c){
	//        System.out.println(a+b+c);
	//    }
	//    public static void method(int a,int b,int c,int d){
	//        System.out.println(a+b+c+d);
	//    }

	}




--4,测试 自动资源管理
	import java.io.*;
	//测试 jdk的新特性
	//jdk1.7  -- try-with-resources:自动关闭流 -- 自动资源管理
	public class TestIO {
	    public static void main(String[] args) {
	//        method();//字节流读取
	        method2();
	    }
	    public static void method2() {
	    //TODO jdk1.7 新语法实现了IO的自动资源管理:
	    // 优化了释放资源的动作,直接把声明的代码放在try的小括号里就行
	        try ( InputStream in = new BufferedInputStream(new FileInputStream("")); ) {
	            //1,创建对象
	            //2,正式读取
	            int b = in.read() ;
	            System.out.println("b = " + b);
	        }catch (Exception e){
	            e.printStackTrace();
	        }
	    }
	    //字节流读取
	    public static void method()  {
	        InputStream in = null ;//2,声明变量,因为finally也要用
	        try {
	            //1,创建对象
	            in = new BufferedInputStream(new FileInputStream(""));
	            //2,正式读取
	            int b = in.read() ;
	            System.out.println("b = " + b);
	            //TODO jdk1.7新语法提供 : 可以一次catch多个类型 的异常 ,中间用 | 分隔
	        }catch (ArithmeticException | IOException e){
	             e.printStackTrace();
	        } finally {  //1,写一定要被执行的代码,否则发生异常时,异常后面的代码根部不会执行!!
	            //3,释放资源
	            try {
	                in.close();
	            } catch (IOException e) {
	                e.printStackTrace();
	            }
	        }
	    }
	}
  • ​ Lambda表达式
//测试
	//测试 jdk的新特性
	//1.8 --Lambda表达式
	//使用Lambda语法来代替 匿名内部类,代码不仅简洁,而且还可读
	//语法:  (参数列表) -> { 方法体 }
	//要求: --只有一个抽象方法的 接口
	public class TestLambda {
	    public static void main(String[] args) {
	        //匿名内部类
	//        Inter in = new Inter(){
	//            //提供重写的抽象方法
	//            @Override
	//            public void save() {
	//                System.out.println("数据保存成功~");
	//            }
	//        } ;
	//        in.save();

	        //TODO Lambda表达式--要求接口里只能有一个抽象方法--优化匿名内部类
	        //TODO 语法:(参数列表) -> { 方法体 } ;  --练习:没有参数,没有返回值
	       Inter in2 = () -> { System.out.println("Lambda表达式!"); } ;
	       in2.save();

	       //TODO 语法:(参数列表) -> { 方法体 } ;   --练习:有参数,没有返回值
	        Inter2 in3 = (int a) -> {  System.out.println(a); } ;
	        in3.delete(10);

	        //TODO 语法:(参数列表) -> { 方法体 } ;   --练习:有参数,有返回值(使用return关键字)
	        Inter3 in4 = (String a,int b) -> { return  a+b ; } ;
	        System.out.println( in4.get("陈子枢",18)  );//打印获取到的结果
	        
	    }
	}
	interface Inter3{
	    String get(String name,int age);
	}
	interface Inter2{
	    void delete(int id) ;
	}
	interface Inter{
	    void save() ;
	}
2、正则表达式regex

–1,概述
就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑对于字符串规定好的一个规则
–2,语法
[0-9][a-z][A-Z] --是指可以出现范围内的数据,只能出现1次
{n} --出现指定的n次
[0-9]{10} --出现数字0-9范围内的就可以,可以出现10次
–3,测试

	import java.util.Scanner;
	//测试  正则表达式
	// 需求: 输入正确的手机号码
	public class TestRegex {
	    public static void main(String[] args) {

	        //接收输入的手机号
	        String input = new Scanner(System.in).nextLine() ;
	        //判断,格式正确吗?---正则表达式--规定正确的字符串的格式
	        String regex = "[1][0-9]{10}" ;
	        //matches()用来判断是否与指定的正则表达式匹配,如果匹配返回true
	        if( input.matches(regex) )
	            System.out.println("手机格式正确...");
	        else
	            System.out.println("手机格式不正确...");

	    }
	}

e();

       //TODO 语法:(参数列表) -> { 方法体 } ;   --练习:有参数,没有返回值
        Inter2 in3 = (int a) -> {  System.out.println(a); } ;
        in3.delete(10);

        //TODO 语法:(参数列表) -> { 方法体 } ;   --练习:有参数,有返回值(使用return关键字)
        Inter3 in4 = (String a,int b) -> { return  a+b ; } ;
        System.out.println( in4.get("陈子枢",18)  );//打印获取到的结果
        
    }
}
interface Inter3{
    String get(String name,int age);
}
interface Inter2{
    void delete(int id) ;
}
interface Inter{
    void save() ;
}




#### 2、正则表达式regex

--1,概述
		就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑对于字符串规定好的一个规则 
--2,语法
	[0-9][a-z][A-Z] --是指可以出现范围内的数据,只能出现1次
	{n} --出现指定的n次
	[0-9]{10} --出现数字0-9范围内的就可以,可以出现10次
--3,测试

```java
	import java.util.Scanner;
	//测试  正则表达式
	// 需求: 输入正确的手机号码
	public class TestRegex {
	    public static void main(String[] args) {

	        //接收输入的手机号
	        String input = new Scanner(System.in).nextLine() ;
	        //判断,格式正确吗?---正则表达式--规定正确的字符串的格式
	        String regex = "[1][0-9]{10}" ;
	        //matches()用来判断是否与指定的正则表达式匹配,如果匹配返回true
	        if( input.matches(regex) )
	            System.out.println("手机格式正确...");
	        else
	            System.out.println("手机格式不正确...");

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值