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();
}
package cn.tedu.ex;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
// 1.正则表达式通常用来规范一个字符串的正确格式
//[abc]--匹配指定范围内的字符,出现一次
//{n}--匹配多次
//接收用户输入的手机号码
String tel = new Scanner(System.in).nextLine();
//规定手机号码的正确格式--String类提供了是否匹配的方法matches,匹配就是true
String regex="1[0-9]{10}";
//判断手机号码的格式正确吗?--正则
if(tel.matches(regex)) {
System.out.println("手机号码格式正确");
}else {
System.out.println("手机号码不正确,请重新输入");
}
}
}
package cn.tedu.ex;
//测试lambda表达式--优化匿名内部类
public class Test2 {
public static void main(String[] args) {
//2.lambda表达--函数式编程
//--语法:(参数列表)->{方法体};
//--要求:接口里包含有一个抽象方法
Inter in2=()->{System.out.println("save()成功了");};
in2.save();//调用save()
Inter2 in3=(int m)->{System.out.println(m+m);};
in3.add(6);
}
}
interface Inter2{
void add(int a);
}
interface Inter{
void save();
}
package cn.tedu.innerclass;
//测试内部类
public class Test1_InnerClass {
public static void main(String[] args) {
//2.使用成员内部类的资源--创建内部类对象
//外部类.内部类 变量名=外部类对象.内部类对象
Outer.Inner oi=new Outer().new Inner();
oi.in();
System.out.println(oi.age);
//4.使用局部内部类的资源--先触发方法
new Outer().show();
}
}
//新建内部类
class Outer{//外部类
String name;
public void show() {
//3.局部位置--局部内部类
class Inner2{
public void test() {
System.out.println("test()....");
}
}
//5.正式开始使用局部内部类的资源
Inner2 inner = new Inner2();
inner.test();
System.out.println("show()....");
}
//1.成员位置--成员内部类
class Inner{
int age=10;
public void in() {
System.out.println("in().....");
}
}
}
package cn.tedu.innerclass;
//测试匿名内部类--最常用 将原来的SSM底层源码里会有大量的出现
public class Test2_AynInnerClass {
public static void main(String[] args) {
//2.测试--接口不能实例化
//3.优化方案:匿名内部类--通常配合匿名对象使用
new Inter() {//创建接口实现类对象
//4.在匿名内部类中,重写接口里的抽象方法
@Override
public void save() {
System.out.println("数据保存成功");
}
}.save();//5.调用方法
//练习匿名内部类的使用
Inter2 in=new Inter2() {
//7.给对象起了一个名字,目的是用一个对象干很多事情
@Override
public void update() {
System.out.println("数据删除成功");
}
@Override
public void delete() {
System.out.println("数据更新成功");
}
};//5.调用方法
in.delete();
in.update();
//9.测试--直接创建抽象类的对象
new Demo(){
//10.重写抽象类中的抽象方法,
@Override
void study() {//10.方法重写时,要有足够权限>=原有权限
System.out.println("study()....");
}
//11.抽象类中的普通方法,必须重写吗?不是,如果需要修改原来业务才重写
}.study();//12.调用方法
}
}
//8.定义抽象类
abstract class Demo{
abstract void study();//使用了默认的修饰符default
public void eat() {
System.out.println("eat()....");
}
}
//1.定义接口
interface Inter {
void save();
}
//6.练习接口
interface Inter2 {
void delete();
void update();
}
package cn.tedu.net;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
//代表Socket通信的客户端
/*职责:
* 1.接收服务器发来的数据
* 2.给服务器发数据
* */
public class Client {
public static void main(String[] args) throws Exception {
// 1.连接指定的服务器--Socket--并且建立通信通道
/*参数1:--要连接的服务器的ip地址
* 参数2:要连接的服务器端口号
* */
Socket socket=new Socket("127.0.0.1",8000);
System.out.println("与服务器连接成功");
//2.给服务器发送hello--out
OutputStream out=socket.getOutputStream();
//3.开始写出数据
out.write("hello".getBytes());//string->byte[]
//把数据刷出去+关闭资源
out.flush();
//4.接收服务器发来的数据--in
InputStream in = socket.getInputStream();
for (int i = 0; i < 5; i++) {
int read = in.read();
//按照字符读就行了,别转成int值
char c=(char)read;
System.out.print(c);
}
//socket.close();
}
}
package cn.tedu.net;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
//代表Socket通信的服务器端
/*职责:
* 1.给客户端发数据
* 2.接收客户端发来的数据
* */
public class Server {
public static void main(String[] args) throws Exception {
//1.开启服务器--ServerSocket--并在8000端口处等待客户端连接请求
ServerSocket server=new ServerSocket(8000);
System.out.println("服务器已启动");
//2.接收客户端发来的连接请求,并建立连接通道
Socket socket=server.accept();
System.out.println("有一个客户端发来了请求");
//3.接收客户端发来的hello--in
InputStream in=socket.getInputStream();
//4.开始读取数据
for (int i = 0; i < 5; i++) {
int read = in.read();
//按照字符读就行了,别转成int值
char c=(char)read;
System.out.print(c);
}
//5.给客户端发出数据--out
OutputStream out = socket.getOutputStream();
out.write("world".getBytes());
//把数据刷出去
out.flush();
}
}