目录
1… Day16–多线程1
1.1 进程
1.1.1 概念
1.1.2 特点
1.2 线程
1.2.1 概念
1.2.2 进程和线程的关系
1.3 多线程的特性
1.3.1 随机性
1.3.2 线程状态
1.4 多线程创建1:继承Thread
1.4.1 概述
1.4.2 常用方法
1.4.3 测试
1.5 多线程创建2:实现Runnable接口
1.5.1 概述
1.5.2 常用方法
1.5.3 测试
1.5.4 比较
1.6 售票案例
1.6.1 方案1:继承Thread
1.6.2 方案2:实现Runnable
1.6.3 问题
2 Day17–多线程2 + 注解
2.1 同步锁
2.1.1 synchronized
2.1.1 特点
2.1.2 改造
2.2 单例设计模式
2.2.1 概念
2.2.2 源码剖析
2.2.3 饿汉式
2.2.4 懒汉式
2.3 注解
2.3.1 概念
2.3.2 分类
2.3.3 JDK注解
2.3.4 元注解
2.4 元注解
2.4.1 @Target ElementType.class
2.4.2 @Retention RetentionPolicy.class
2.5 自定义注解
2.5.1 定义注解
2.5.2 使用注解
2.5.3 解析注解
3 Day18–反射 + 内部类
3.1 反射
3.1.1 概念
3.1.2 为什么需要反射
3.1.3 反射Class类对象
3.1.4 常用方法
3.2 反射的应用
3.2.1 获取类对象
3.2.2 获取构造方法
3.2.3 获取成员方法
3.2.4 获取成员变量
3.2.5 创建对象
3.3 暴力反射
3.3.1 创建Person类
3.3.2 测试
3.4 内部类
3.4.1 概述
3.4.2 特点
3.4.3 成员内部类
3.4.4 匿名内部类
4 Day19–socket
4.1 网络
4.2 Socket
4.2.1 概述
4.3 服务器端-ServerSocket
4.4 客户端-Socket
4.5 入门案例
4.5.1 服务器端
4.5.2 客户端
5 Day20–复习
1 Day16–多线程1
1.1 进程
1.1.1 概念
就是正在运行的程序。也就是代表了程序锁占用的内存区域。
1.1.2 特点
l 独立性:进程是系统中独立存在的实体,它可以拥有自己的独立的资源,每一个进程都拥有自己私有的地址空间。在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间。
l 动态性:进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。在进程中加入了时间的概念,进程具有自己的生命周期和各种不同的状态,这些概念在程序中都是不具备的。
l 并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。
1.2 线程
1.2.1 概念
线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个进程可以开启多个线程。
多线程扩展了多进程的概念,使得同一个进程可以同时并发处理多个任务。
简而言之,一个程序运行后至少一个进程,一个进程里包含多个线程。
如果一个进程只有一个线程,这种程序被称为单线程。
如果一个进程中有多条执行路径被称为多线程程序。
1.2.2 进程和线程的关系
从上图中可以看出一个操作系统中可以有多个进程,一个进程中可以有多个线程,每个进程有自己独立的内存,每个线程共享一个进程中的内存,每个线程又有自己独立的内存。(记清这个关系,非常重要!)
所以想使用线程技术,得先有进程,进程的创建是OS创建的,你能实现吗?不能,一般都是c或者c++语言完成的。
1.3 多线程的特性
1.3.1 随机性
3.2 线程状态
线程生命周期,总共有五种状态:
-
新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();
-
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
-
运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
-
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态;
-
根据阻塞产生的原因不同,阻塞状态又可以分为三种:
a) 等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
b) 同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
c) 其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
6) 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
1.4 多线程创建1:继承Thread
1.4.1 概述
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。Start()方法是一个native方法,它将通知底层操作系统,最终由操作系统启动一个新线程,操作系统将执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。
模拟开启多个线程,每个线程调用run()方法
1.4.2 常用方法
String getName()
返回该线程的名称。
static Thread currentThread()
返回对当前正在执行的线程对象的引用。
void setName(String name)
改变线程名称,使之与参数 name 相同。
static void sleep(long millis)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
Thread(String name)
分配新的 Thread 对象。
1.4.3 测试
package seday13new;
public class Test1 {
public static void main(String[] args) {
//3、创建线程对象
ThreadDemo t1 = new ThreadDemo("钢铁侠");
ThreadDemo t2 = new ThreadDemo("美队");
//4、开启线程:谁抢到资源谁就先执行
t1.start();
t2.start();
//t1.run();//当做常规方法调用,且 不会发生多线程现象
}
}
//1、作为Thread的子类,并重写run方法。把多线程的业务写在run方法中
class ThreadDemo extends Thread{
public ThreadDemo() {}
public ThreadDemo(String name) {
super(name);
}
@Override
public void run() {
//2、默认实现是super.run();
for (int i = 0; i < 10; i++) {
System.out.println(getName()+i);
}
}
}
执行结果:
hello0
hello1
hello2
hello3
hello4
hello5
hello6
hello7
hello8
hello1
hello2
hello3
hello9
hello4
hello5
hello6
hello7
hello8
hello9
注意:从上面结果可以确认,start()方法只是通知操作系统线程就绪,具体什么时间执行,操作系统来决定,我们JVM已经控制不了了。
1.5 多线程创建2:实现Runnable接口
1.5.1 概述
如果自己的类已经extends另一个类,就无法多继承,此时,可以实现一个Runnable接口。
1.5.2 常用方法
void run()
使用实现接口 Runnable 的对象创建一个线程时,启动该线程将导致在独立执行的线程中调用对象的 run 方法。
1.5.3 测试
package seday13new;
public class Test2 {
public static void main(String[] args) {
MyThread t = new MyThread ();
//2,构造创建对象,传入Runnable子类
Thread target = new Thread(t);
Thread target2 = new Thread(t);
//开启线程
target.start();
target2.start();
}
}
//1,实现Runnable接口,重写run()
class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
执行结果:
Thread-0
Thread-2
Thread-1
Thread-5
Thread-8
Thread-6
Thread-7
Thread-4
Thread-3
Thread-9
注意:可以看到执行顺序是乱的,我们已经知道start()方法只是通知操作系统线程就绪,具体什么时间执行,操作系统来决定,我们JVM已经控制不了了。这就是乱序的原因,也是正常的。
1.5.4 比较
1.6.1 方案1:继承Thread
package seday13new;
public class Test3 {
public static void main(String[] args) {
Ticket t = new Ticket();
Ticket t2 = new Ticket();
Ticket t3 = new Ticket();
Ticket t4 = new Ticket();
//问题:票好像卖重复了,同一张票卖了好多次...
t.start();
t2.start();
t3.start();
t4.start();
}
}
class Ticket extends Thread {
//卖了200张票,变成static的
static private int tic = 100;
@Override
public void run() {
while (true) {
//tic=1时,谁都可以进来,t t2 t3 t4
if (tic > 0) {
try {
//t t2 t3 t4都睡了
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//t醒了,tic--=1,tic=0;
//t2醒了,tic--=0,tic=-1;
//t3醒了,tic--=-1,tic=-2;
//t4醒了,tic--=-2,tic=-3;
System.out.println(tic--);
}
}
}
}
1.6.2 方案2:实现Runnable
package seday13new;
public class Test4 {
public static void main(String[] args) {
//只创建一次,就100张票
Ticket2 t = new Ticket2();
Thread target = new Thread(t,"窗口1");
Thread target2 = new Thread(t,"窗口2");
Thread target3 = new Thread(t,"窗口3");
Thread target4 = new Thread(t,"窗口4");
target.start();
target2.start();
target3.start();
target4.start();
}
}
class Ticket2 implements Runnable{
private int tickets=100;
@Override
public void run() {
while(true) {
if(tickets >0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+tic--);
}
}
}
}
1.6.3 问题
1、 每次创建线程对象,都会生成一个tickets变量值是100,创建4次对象就生成了400张票了。不符合需求,怎么解决呢?能不能把tickets变量在每个对象间共享,就保证多少个对象都是卖这100张票。-- 用静态修饰
2、 产生超卖,-1张、-2张。
3、 产生重卖,同一张票卖给多人。
4、 多线程安全问题是如何出现的?常见情况是由于线程的随机性+访问延迟。
5、 以后如何判断程序有没有线程安全问题?在多线程程序中+有共享数据+多条语句操作共享数据。
2 Day17–多线程2 + 注解
2.1 同步锁
把有可能出现问题的代码包起来,一次只让一个线程执行。通过sychronized关键字实现同步。
当多个对象操作共享数据时,可以使用同步锁解决线程安全问题。
2.1.1 synchronized
synchronized(对象){
需要同步的代码;
}
2.1.1 特点
1、 前提1,同步需要两个或者两个以上的线程。
2、 前提2,多个线程间必须使用同一个锁。
3、 同步的缺点是会降低程序的执行效率, 为了保证线程安全,必须牺牲性能。
4、 可以修饰方法称为同步方法,使用的锁对象是this。
5、 可以修饰代码块称为同步代码块,锁对象可以任意。
2.1.2 改造
package seday13new;
public class Test4 {
public static void main(String[] args) {
Ticket2 t = new Ticket2();
Thread target = new Thread(t, "窗口1");
Thread target2 = new Thread(t, "窗口2");
Thread target3 = new Thread(t, "窗口3");
Thread target4 = new Thread(t, "窗口4");
target.start();
target2.start();
target3.start();
target4.start();
}
}
class Ticket2 implements Runnable {
private int tic = 100;
Object obj = new Object();
@Override
public void run() {
while (true) {
// 把有线程安全问题的代码,用同步关键字包起来
// 原理:用一个对象作为一把锁,给代码上锁,一个线程访问锁代码时,其他线程只能等待锁释放才能进来。
// 多线程间要使用同一把锁才可以真的把代码锁住实现线程安全。
// synchronized (new Object()) {//锁了不同对象
// synchronized (obj) {//锁了同一个对象
//synchronized (Ticket2.class) {//锁了本类,针对于静态
synchronized (this) {
if (tic > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(tic--);
}
}
}
}
}
2.2 单例设计模式
2.2.1 概念
单例模式可以说是大多数开发人员在实际中使用最多的,常见的Spring默认创建的bean就是单例模式的。
单例模式有很多好处,比如可节约系统内存空间,控制资源的使用。
其中单例模式最重要的是确保对象只有一个。
简单来说,保证一个类在内存中的对象就一个。
RunTime就是典型的单例设计,我们通过对RunTime类的分析,一窥究竟。
2.2.2 源码剖析
/**
* Every Java application has a single instance of class
* <code>Runtime</code> that allows the application to interface with
* the environment in which the application is running. The current
* runtime can be obtained from the <code>getRuntime</code> method.
* <p>
* An application cannot create its own instance of this class.
*
* @author unascribed
* @see java.lang.Runtime#getRuntime()
* @since JDK1.0
*/
RunTime.java
package java.lang;
public class Runtime {
//1、创建静态的全局唯一的对象
private static Runtime currentRuntime = new Runtime();
//2、私有构造方法,不让外部来调用
/** Don't let anyone else instantiate this class */
private Runtime() {}
//3、通过自定义的静态方法获取实例
public static Runtime getRuntime() {
return currentRuntime;
}
}
2.2.3 饿汉式
package cn.tedu.design;
//测试单例设计模式--就是按照一定的开发步骤,按照一定的模板进行开发,达到程序中只会有一个实例在干活的目的!!
public class Test5_Design {
public static void main(String[] args) {
//4,测试 new多少次都是一个对象???--
// MySingleTon m = new MySingleTon();--构造方法私有化
MySingleTon m1 = MySingleTon.getMy();
MySingleTon m2 = MySingleTon.getMy();
System.out.println(m1==m2);//是同一个对象吗???
System.out.println(m1.equals(m2));//默认用Object的==
}
}
class MySingleTon {
//1,私有化改造方法 -- 目的就是控制外界创建对象的权利
private MySingleTon() {}
//2,封装创建好的对象 -- 封装,不让外界随意看到我创建的对象
static private MySingleTon my = new MySingleTon();
//3,提供公的获取对象的方法
//静态方法,因为没法new了,还想用,就用类名访问--修饰成静态的
static public MySingleTon getMy(){
return my;//静态只能调静态
}
}
2.2.4 懒汉式
//懒汉式 -- 面试重点!!延迟加载思想+线程不安全
class MySingleTon2 {
// 1,私有化改造方法 -- 目的就是控制外界创建对象的权利
private MySingleTon2() {
}
// 2,封装创建好的对象 -- 先不创建对象,啥时候用啥时候创建!!
static private MySingleTon2 my;
// 3,提供公的获取对象的方法
// 静态方法,因为没法new了,还想用,就用类名访问--修饰成静态的
synchronized static public MySingleTon2 getMy() {//b,方法里都是同步代码,可以是同步方法
// synchronized (MySingleTon2.class) {//a,同步代码块,静态方法的锁,是类名.class本类的字节码对象
// t1,t2,t3来准备new
if (my == null) {
// t1 new
// t2 new
// t3 new
my = new MySingleTon2();// 开始创建!! 延迟加载
}
// }
return my;// 静态只能调静态
}
}
2.3 注解
2.3.1 概念
注解很厉害,它可以增强我们的java代码,同时利用反射技术可以扩充实现很多功能。它们被广泛应用于三大框架底层。传统我们通过xml文本文件声明方式,而现在最主流的开发都是基于注解方式,代码量少,框架可以根据注解去自动生成很多代码,从而减少代码量,程序更易读。例如最火爆的SpringBoot就完全基于注解技术实现。
注解设计非常精巧,初学时觉得很另类甚至多余,甚至垃圾。有了java代码干嘛还要有@注解呢?但熟练之后你会赞叹,它竟然可以超越java代码的功能,让java代码瞬间变得强大。大家慢慢体会吧。
常见的元注解:@Target、@Retention,jdk提供将来描述我们自定义的注解的注解。听起来好绕,别着急,做两个例子,立刻清晰。现在现有“元注解”这个概念。
2.3.2 分类
l JDK自带注解
l 元注解
l 自定义注解
2.3.3 JDK注解
JDK注解的注解,就5个:
l @Override
l @Deprecated标记就表明这个方法已经过时了,但我就要用,别提示我过期
l @SuppressWarnings(“deprecation”) 忽略警告
l @SafeVarargs jdk1.7出现,堆污染,不常用
l @FunctionallInterface jdk1.8出现,配合函数式编程拉姆达表达式,不常用
2.3.4 元注解
描述注解的注解,就5个:
l @Target 注解用在哪里:类上、方法上、属性上
l @Retention 注解的生命周期:源文件中、class文件中、运行中
l @Inherited 允许子注解继承
l @Documented 生成javadoc时会包含注解,不常用
l @Repeatable注解为可重复类型注解,可以在同一个地方多次使用,不常用
2.4 元注解
2.4.1 @Target ElementType.class
描述注解的使用范围:
l ElementType.ANNOTATION_TYPE 应用于注释类型
l ElementType.CONSTRUCTOR 应用于构造函数
l ElementType.FIELD 应用于字段或属性
l ElementType.LOCAL_VARIABLE 应用于局部变量
l ElementType.METHOD 应用于方法级
l ElementType.PACKAGE 应用于包声明
l ElementType.PARAMETER 应用于方法的参数
l ElementType.TYPE 应用于类的元素
2.4.2 @Retention RetentionPolicy.class
定义了该注解被保留的时间长短,某些注解仅出现在源代码中,而被编译器丢弃;
而另一些却被编译在class文件中; 编译在class文件中的注解可能会被虚拟机忽略,而另一些在class被装载时将被读取。
为何要分有没有呢?没有时,反射就拿不到,从而就无法去识别处理。
l SOURCE 在源文件中有效(即源文件保留)
l CLASS 在class文件中有效(即class保留)
l RUNTIME 在运行时有效(即运行时保留)
2.5 自定义注解
2.5.1 定义注解
//1,定义注解
//1.1,设置注解的使用范围@Target,啥都不写,哪儿都能用
//@Target({ElementType.METHOD})//作用于方法上
//@Target({ElementType.FIELD})//作用于属性上
@Target({ElementType.METHOD , ElementType.PACKAGE})//作用范围
@Retention(RetentionPolicy.SOURCE)//生命周期
@Target({ElementType.TYPE})//作用于类上
@interface Test{
//3,定义属性
int age() default 0;//使用时,必须给age属性赋值,如:age=X。除非设置好默认值。
//()不是参数,也不能写参数,只是特殊语法
//4,特殊属性value
String value() default "";//使用时,必须给value属性赋值,如:X | value=X。除非设置好默认值
}
注意:注解的语法写法和常规java的语法写法不同
2.5.2 使用注解
//2,使用注解
//@Test
//5,注解的组合属性
@Test(value="",age=0)
class HelloTest{
// @Test(value="",age=0)
String name;
}
2.5.3 解析注解
判断注解是否存在
package javapro.spring.annotation;
public class TestAnnotation {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> clazz = Class.forName("javapro.spring.annotation.HelloController");
Controller c = clazz.getAnnotation(Controller.class);
if( c != null) {
System.out.println(c);
System.out.println(c.value());
}
}
}
3 Day18–反射 + 内部类
3.1 反射
3.1.1 概念
Reflection(反射) 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,也有称作“自省”。反射非常强大,它甚至能直接操作程序的私有属性。我们前面学习都有一个概念,private的只能类内部访问,外部是不行的,但这个规定被反射赤裸裸的打破了。
反射就像一面镜子,它可以在运行时获取一个类的所有信息,可以获取到任何定义的信息(包括成员变量,成员方法,构造器等),并且可以操纵类的字段、方法、构造器等部分。
3.1.2 为什么需要反射
好好的我们new User(); 不是很好,为什么要去通过反射创建对象呢?
那我要问你个问题了,你为什么要去餐馆吃饭呢?
例如:我们要吃个牛排大餐,如果我们自己创建,就什么都得管理。
好处是,每一步做什么我都很清晰,坏处是什么都得自己实现,那不是累死了。牛接生你管,吃什么你管,屠宰你管,运输你管,冷藏你管,烹饪你管,上桌你管。就拿做菜来说,你能有特级厨师做的好?
那怎么办呢?有句话说的好,专业的事情交给专业的人做,饲养交给农场主,屠宰交给刽子手,烹饪交给特级厨师。那我们干嘛呢?
我们翘起二郎腿直接拿过来吃就好了。
再者,饭店把东西做好,不能扔到地上,我们去捡着吃吧,那不是都成原始人了。那怎么办呢?很简单,把做好的东西放在一个容器中吧,如把牛排放在盘子里。
在开发的世界里,spring就是专业的组织,它来帮我们创建对象,管理对象。我们不在new对象,而直接从spring提供的容器中beans获取即可。Beans底层其实就是一个Map<String,Object>,最终通过getBean(“user”)来获取。而这其中最核心的实现就是利用反射技术。
总结一句,类不是你创建的,是你同事或者直接是第三方公司,此时你要或得这个类的底层功能调用,就需要反射技术实现。有点抽象,别着急,我们做个案例,你就立马清晰。
3.1.3 反射Class类对象
Class.forName(“类的全路径”);
类名.class
对象.getClass();
3.1.4 常用方法
获得包名、类名
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(实例, 参数数据) ;//让指定的实例来执行该方法
3.2 反射的应用
3.2.1 获取类对象
private static void method() throws Exception {
Class clazz = Student.class;
Class<?> clazz2 = Class.forName(“seday15.Student”);
Class clazz3 = new Student().getClass();
System.out.println(clazz.getName());
System.out.println(clazz2.getName());
System.out.println(clazz3.getName());
}
3.2.2 获取构造方法
private static void method3(Class clazz) {
Constructor[] cs = clazz.getDeclaredConstructors();
for (Constructor c : cs) {
String name = clazz.getSimpleName();
System.out.println(name);
Class[] cs2 = c.getParameterTypes();//参数
System.out.println(Arrays.toString(cs2));
}
}
3.2.3 获取成员方法
private static void method4(Class clazz) {
Method[] ms = clazz.getMethods();
for (Method m : ms) {
String name = m.getName();
System.out.println(name);
Class<?>[] cs = m.getParameterTypes();
System.out.println(Arrays.toString(cs));
}
}
3.2.4 获取成员变量
private static void method2(Class clazz) {
Field[] fs = clazz.getFields();//获取public的属性
for (Field f : fs) {
String name = f.getName();
String tname = f.getType().getSimpleName();
System.out.println(name);
System.out.println(tname);
}
}
3.2.5 创建对象
package seday15;
import java.lang.reflect.Constructor;
import java.util.Scanner;
//反射新建两个对象
public class Test3 {
public static void main(String[] args) throws Exception {
String s = new Scanner(System.in).nextLine();
Class<?> clazz = Class.forName(s);
Object o1 = clazz.newInstance();//用无参构造
System.out.println(o1);
Constructor<?> c = clazz.getConstructor(String.class);//用含参构造
Object o2 = c.newInstance("jack");
System.out.println(o2);
}
}
3.3 暴力反射
指可以将程序中的私有的属性或者方法通过反射技术,暴力的获取到资源。需要使用的常见方法如下:
3.3.1 创建Person类
class Person{
private String name="jack";
private int age = 30;
private void show(int[] a) {
System.out.println("show()..."+Arrays.toString(a));
}
private void test() {
System.out.println("test()...");
}
}
3.3.2 测试
1、 获取私有属性值并修改
2、 获取私有方法并执行
package seday16new;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test3_ReflectPerson {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("seday16new.Person");
// method(clazz);//隐私属性
method2(clazz);//执行方法
}
private static void method2(Class<?> clazz) throws Exception {
Method m = clazz.getDeclaredMethod("show", int[].class);
Object obj = clazz.newInstance();
m.setAccessible(true);//方法隐私可见
m.invoke(obj, new int[]{1,2,3});//执行
}
private static void method(Class clazz) throws Exception {
Field f = clazz.getDeclaredField("name");
System.out.println(f.getType().getName());
f.setAccessible(true);//属性隐私可见
Object obj = clazz.newInstance();
// f.set(obj, "rose");//设置值
System.out.println(f.get(obj));//获取值
//---所有属性
Field[] fs = clazz.getDeclaredFields();
for (Field ff : fs) {
System.out.println(ff);
ff.setAccessible(true);//暴力反射
System.out.println(ff.get(obj));
}
}
}
3.4 内部类
3.4.1 概述
如果一个类存在的意义就是为指定的另一个类,可以把这个类放入另一个类的内部。就是把类定义在类的内部的情况就可以形成内部类的形式。
A类中又定义了B类,B类就是内部类。B类可以当做A类的一个成员看待。
3.4.2 特点
1、 内部类可以直接访问外部类中的成员,包括私有成员
2、 外部类要访问内部类的成员,必须要建立内部类的对象
3、 在成员位置的内部类是成员内部类
4、 在局部位置的内部类是局部内部类
3.4.3 成员内部类
被private修饰
package cn.tedu.inner;
//测试内部类被private修饰
public class Test5_InnerClass2 {
public static void main(String[] args) {
//TODO 创建内部类对象,并执行show()
// Outer2.Inner2 oi = new Outer2().new Inner2();//报错,Inner2已经被private了
//3,测试被private的内部类的资源能否执行!
new Outer2().test();
}
}
class Outer2{
//2,如果想要访问private的内部类,可以访问外部类提供的对应方法
public void test() {
//访问内部类方法
new Inner2().show();
}
//位置在类里方法外--成员内部类
//1,内部类可以被private修饰,但是外界无法直接创建对象了!
private class Inner2{
public void show() {
System.out.println("Inner2.show()");
}
}
}
被static修饰
package cn.tedu.inner;
//测试内部类被static修饰
public class Test6_InnerClass3 {
public static void main(String[] args) {
// 创建内部类对象测试show()
// Outer3.Inner3 oi = new Outer3().new Inner3();//报错,原因是Inner3是静态的内部类
Outer3.Inner3 oi = new Outer3.Inner3();//Outer3.Inner3通过类名.调用类中的静态资源
oi.show();
Outer3.Inner3.show2();//调用静态内部类里的静态方法
}
}
class Outer3{
//1,内部类被static修饰--随着类的加载而加载,会造成内存资源浪费,并不常用!
static class Inner3{
public void show() {
System.out.println("Inner3.show()");
}
static public void show2() {
System.out.println("Inner3.show2()");
}
}
}
3.4.4 匿名内部类
匿名内部类属于局部内部类,并且是没有名字的内部类。
package cn.tedu.inner;
//测试匿名内部类
public class Test8_InnerClass5 {
public static void main(String[] args) {
new Hello() {// 匿名对象,本身接口不能new,这里new Hello()匿名对象,就相当于Hello接口的实现类
// 匿名内部类
@Override
public void save() {
System.out.println("save()..");
}
@Override
public void update() {
System.out.println("update()..");
}
}.update();// 触发指定的方法
new Hello2() {//抽象类的匿名内部类
@Override
public void show() { }
}.show();
new Animal() {//普通类的匿名内部类
@Override
public void eat() { }
};
}
}
//TODO 创建匿名对象+匿名内部类测试
class Animal{
public void eat() {}
}
abstract class Hello2 {
abstract public void show();
public void delete() { }
}
// 定义接口
interface Hello {
void save();
void update();
}
4 Day19–socket
4.1 网络
查看本机ip地址
4.2 Socket
4.2.1 概述
也叫套接字编程,是一个抽象层。
应用程序可以通过它发送或接收数据,可对其像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口与协议的组合。
Socket就是为网络编程提供的一种机制 / 通信的两端都有Socket
网络通信其实就是Socket间的通信 / 数据在两个Socket间通过IO传输
4.3 服务器端-ServerSocket
在服务器端,选择一个端口号,在指定端口上等待客户端发起连接。
启动服务:ServerSocket ss = new ServerSocket(端口);
等待客户端发起连接,并建立连接通道:Sokcet socket = ss.accept();
4.4 客户端-Socket
新建Socket对象,连接指定ip的服务器的指定端口
Socket s = new Socket(ip, port);
从Socket获取双向的流
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
4.5 入门案例
服务器端接收客户端发来的hello,并给客户端响应hello。
4.5.1 服务器端
说明其中,server端的accept()是阻塞的,客户端不连接,服务器不执行后面流程。
in.read()也是阻塞的,读不到就死等。
package seday16;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws Exception {
//1,在指定端口启动服务
ServerSocket server = new ServerSocket(8000);
//2,等待客户端发起连接,并建立通道
Socket socket = server.accept();
//3,取出双向的流
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
//4,通信
/*
* 通信协议:
* 1,通信流程
* 2,数据格式
* 先接收hello,在发送world
*/
//接收客户端发来的hello
for(int i = 0 ; i < 5 ;i++) {
//一个一个字节从网络流中读取客户端发来的数据
char c = (char) in.read();
System.out.print(c);//一行展示收到的数据
}
//给客户端发送数据
out.write("world".getBytes());
out.flush();//刷出内存缓存
//释放资源
socket.close();//断开连接
server.close();//释放端口
}
}
4.5.2 客户端
package seday16;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws Exception, IOException {
//1,与服务器建立连接
//同桌启动服务器,你当客户端发送
Socket s = new Socket("127.0.0.1",8000);
//2,取出双向的流
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
//3,通信
/*
* 先发送Hello,再接收world
*/
//给服务器发送数据
out.write("hello".getBytes());
out.flush();
//接收服务器响应的数据
for (int i = 0; i < 5 ; i++) {
char c = (char) in.read();
System.out.print(c);//同一行展示
}
//释放资源
s.close();
}
}