java基础语法
##java基础语法
###重写
参数列表与被重写方法的参数列表必须完全相同。
返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
父类的成员方法只能被它的子类重写。
声明为 final 的方法不能被重写。
声明为 static 的方法不能被重写,但是能够被再次声明。
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
构造方法不能被重写。
如果不能继承一个类,则不能重写该类的方法。
##java反射
###反射基本概念
官方概念:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法,这种动态获取、调用对象方法的功能称为java语言的反射机制。
反射是通过Class对象(字节码文件),来知道某个类的所有属性和方法。也就是说通过反射我们可以获取构造器,对象,属性,方法(原本不知道)
我的理解:就是获取实例化对象的原本的class。那么此时我们获取到他原本的class以后,想要获取其属性,方法就很容易了。
###获取Class对象
####通过该类的对象获取对应的Class对象
public static void main(String[] args) throws Exception{ |
####通过类名.class静态属性
Class stuClass2 = Student.class; |
虽然比较简单,但是需要导包,不然会编译错误
####通过Class类中的静态方法forName()方法获取
public static void main(String[] args) throws Exception{ |
###获取有参构造方法
####getConstructor()
public Constructor<T> getConstructor(Class<?>... parameterTypes) |
这里解释一下,
1.首先传出的类型和Constructor有关,这个<T>指的是泛型,意思是传入的类型又右边的数据类型决定
2.后面可以发现,他传入的数据类型要求是一个class类,那么我们这里其实可以这样构造
import java.io.*; |
有几个点解释一下:
- newInstance() 是java反射框架中类对象(Class)创建新对象的方法
- 现在获取到person这个原型Class,然后我们想要实例化对象出来(也就是利用),此时我们可以单纯使用newInstance,但是这样的话,此时调用的是person类的无参构造方法
public static void main(String[] args) throws Exception{
person p=new person("test");
Class a =Class.forName("person");
person pe1=(person) a.newInstance();
} - 可以使用getConstructor获取其有参构造方法,而里面的参数由于要求写的是类,所以我们的写法是String.class,即通过类名.class静态属性获取其原型Class进行穿参写入
- 接下来再使用newInstance创建的就是有参的对象了
###获取类里面的属性
####getFields()&getDeclaredFields()
这两个就是打印当前类里面的所有属性,但是加上Declared以后其私有属性也将会被打印出来####getField()&getDeclaredField()public static void main(String[] args) throws Exception{
person p=new person("test",22);
Class a =Class.forName("person");
// Constructor personconstructor=a.getConstructor(String.class);
// person pe1=(person) personconstructor.newInstance("aaa");
// System.out.println(pe1);
person pe1=(person) a.newInstance();
Field[] persof=a.getFields();
// Field[] persof=a.getDeclaredFields();
for(Field f:persof){
System.out.println(f);
}
}
更改public属性:
public static void main(String[] args) throws Exception{ |
- 使用set进行赋值操作,可以看到他的参数为obj和value,也就是说我们需要对一个对象进行赋值操作
更改private属性:
public static void main(String[] args) throws Exception{ |
只需要使用getDeclaredField,并且加上setAccessible(true)
###获取类的方法
####getDeclaredMethod()/getMethod()Method perMethod=a.getMethod("getName");
System.out.println(perMethod.invoke(p))和上面获取属性的用法类似
这里需要使用invoke来启动这个方法
调用有参方法,需要写清楚类型
Method perMethod=a.getMethod("getName", String.class);
System.out.println(perMethod.invoke(p,"abcd"));获取private方法,也只需要加上perMethod.setAccessible(true)即可触发
##java代理
###java静态代理
若类中方法较多,则静态代理类中需要重写的方法也较多public class UserProxy implements IUser {
IUser user;
public UserProxy(){}
public UserProxy(IUser user){this.user = user;}
public void show() {
user.show();
System.out.println("调用了show");
}
}###java动态代理
####构造过程
demo
main.java:public class ProxyTest {
public static void main(String[] args) {
IUser user=new UserImpl();
//user.show();
//静态代理
// IUser userproxy = new UserProxy();
// userproxy.show();
InvocationHandler userInvocationHandler = new UserInvocationHandler(user);
IUser userproxy = (IUser) Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(),userInvocationHandler);
userproxy.show();
}
}UserinvocationHandler:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class UserInvocationHandler implements InvocationHandler {
IUser user;
public UserInvocationHandler(IUser user){
this.user = user;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(user,args);
return null;
}
}动态代理利用的是java中的Proxy类来进行代理,其参数由三部分组成,其中最主要的是要自定义一个InvocationHandler,并通过重写其invoke方法来进行动态代理调用,从而弥补静态代理过程中如果有多个方法,需要重写多个方法的缺陷
动态代理使用过程中,除了一开始创建InvocationHandler的时候调用的是InvocationHandler接口,其他的都是属于你所要调用的类的接口,例如这个demo中是IUser,所以后面写的都是IUser
####意义
代理的意义,其实就是不修改原有的类,但是增加了其功能
##java类加载机制
###类加载的时候会执行代码情况
初始化:静态代码块
实例化:构造代码块,无参构造函数
####动态类加载方法
Classloader将字节流加载到内存,并使用defineClass加载到JVM生成可以被调用的类。Java源码编译之后生成对应的字节码,字节码的存储形式不只局限于文件,还可以使用访问数据库,URL请求的方式进行获取。存储的字节码还可以使用加密算法进行加密,提高存储安全性1.Class.forName 不能加载原生类型,但其他类型都是支持的
2.Classloader.loadclass 不能加载原生类型和数组类型,其他类型都是支持的
Class.forname
ClassLoader->SecureClassLoader->URLClassLoader->AppClassLoader
LoadClass->findClass(重写的方法)->defineClass(从字节码加载类)
public class LoadClassTest { |
defineClass重载加载字节码
public class LoadClassTest { |
unsafe实现动态类加载
Class c = Unsafe.class; |