21、什么是反射机制
我们以前都是使用new对象的方式来创建一个类的对象,但实际应用中,我们可能不知道到底使用哪个类,我们希望可以动态的获取类的信息,可以动态的创建对象、使用它的方法和参数等等。这就用到了反射机制,反射机制是java的一个重要的特性,它允许我们在运行期间,使用Reflection API来动态获取类的信息,从而提高程序的灵活性。在运行期间已经变为class字节码文件可以反编译从而获取到实际类的信息。
Dog类:
public class Dog {
private String dogName;
private Integer dogAge;
public Dog() {
}
public Dog(String dogName, Integer dogAge) {
this.dogName = dogName;
this.dogAge = dogAge;
}
public String getDogName() {
return dogName;
}
public void setDogName(String dogName) {
this.dogName = dogName;
}
public Integer getDogAge() {
return dogAge;
}
public void setDogAge(Integer dogAge) {
this.dogAge = dogAge;
}
public void bark(){
System.out.println(dogName + " is barking");
}
@Override
public String toString() {
return "Dog{" +
"dogName='" + dogName + '\'' +
", dogAge=" + dogAge +
'}';
}
}
这里就是用到反射机制来操作Dog类。
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
Class clazz = Dog.class;
Constructor catConstructor = clazz.getConstructor(String.class, Integer.class);
Dog tom = (Dog) catConstructor.newInstance("Tom", 22);
Method bark = clazz.getDeclaredMethod("bark");
bark.invoke(tom);
}
22、什么是java序列化、什么情况下需要序列化
当我们希望通过网络传递java对象,或者把java对象持久化的时候,就可以用到java序列化了。这也就是一种通过IO流的方式,把java对象变为流序列化传输,再通过流的方式反序列化获取得到这个对象。
public class SeriableTest {
public static void main(String[] args) {
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\object.txt"));
User user = new User("张三",24);
oos.writeObject(user);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\object.txt"));
User user1 = (User) ois.readObject();
System.out.println(user1);
} catch (IOException e) {
e.printStackTrace();
}catch (ClassNotFoundException e){
e.printStackTrace();
}
}
}
23、动态代理怎么实现的?应用在哪些场景?
动态代理就是当需要对某个类中的方法进行扩展时,加入的一些额外处理。比如添加日志、事务等等操作。相当于给这个类创建了一个代理类,让这个代理类去包含原有的方法、并添加扩展的操作。这个类不是我们写好的,而是动态生成的,所以这个就叫做动态代理。
动态代理怎么实现,有两种方式,一种是jdk动态代理,使用在有接口的类中、还有一种是cglib动态代理这种方式使用在普通的类上。
jdk动态代理想要实现InvocationHandler接口,并且实现它里面的invoke方法,这个方法是给原有类的方法进行扩展的。
创建接口:
public interface Person{
public void sayHi();
}
创建接口实现类:
public class Children implements Person{
@Override
public void sayHi() {
System.out.println("I am a child,nice to meet you");
}
}
创建代理类和代理逻辑:
public class Proxy implements InvocationHandler {
//真实对象
private Object target = null;
/**
* 建立目标对象与代理对象之间的联系
* @param target 目标对象
* @return
*/
public Object bind(Object target){
this.target = target;
return java.lang.reflect.Proxy.newProxyInstance
(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
/**
* 代理方法的逻辑
* @param proxy 代理对象
* @param method 方法
* @param args 方法参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用代理方法");
System.out.println("真实方法前面的操作");
Object obj = method.invoke(target,args);
System.out.println("真实方法之后的操作");
return null;
}
}
测试:
public class TestProxy {
public static void main(String[] args) {
Proxy proxy1 = new Proxy();
Person person = (Person) proxy1.bind(new Children());
person.sayHi();
}
}
---------------------------------------------------------------
调用代理方法
真实方法前面的操作
I am a child,nice to meet you
真实方法之后的操作
cglib动态代理是使用在没有接口的类中,创建代理类让它实现MethodInterceptor的intercept方法。这里需要导入cglib和asm的jar包。
实体类:
public class Animal {
public void run(){
System.out.println("跑");
}
}
代理类:
public class AnimalProxy implements MethodInterceptor {
/**
* 生成cglib代理对象
* @return
*/
public Object getProxy(Class cls){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(cls);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("调用真实对象之前");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("调用真实对象之后");
return result;
}
}
测试:
public class TestProxy {
public static void main(String[] args) {
AnimalProxy proxy = new AnimalProxy();
Animal animal = (Animal) proxy.getProxy(Animal.class);
animal.run();
}
}
-----------------------------------------------------------
调用真实对象之前
跑
调用真实对象之后
应用场景:spring-aop、统一日志输出、spring事务等等。
24、为什么要克隆对象
①方便,克隆的对象可能包含一些已经修改过的属性,而new出来的对象的属性都还是初始化时候的值,所以当需要一个新的对象来保存当前对象的“状态”使用clone方式很方便; ②速度快,clone方法最终将调用JVM中的原生方法完成复制也就是调用底层的c++代码,所以一般使用clone方法复制对象要比新建一个对象然后逐一进行元素复制效率要高;
25、如何实现对象的克隆?
让类实现cloneable接口的clone方法。
public class Person implements Cloneable{
private String name;
private Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试:
public class TestPerson {
public static void main(String[] args) throws CloneNotSupportedException {
Person p1 = new Person("zhangsan",100);
Person p2 = (Person) p1.clone();
System.out.println("修改前......");
System.out.println(p1);
System.out.println(p2);
System.out.println("修改后......");
p1.setAge(80);
System.out.println(p1);
System.out.println(p2);
}
}
26、深拷贝和浅拷贝?
深拷贝和浅拷贝。
浅拷贝:拷贝的对象还保留原有对象的引用地址。这样修改任何一个就值就会都改。
深拷贝:拷贝的对象产生了新的对象。
浅拷贝:
public class{
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}public class Person implements Cloneable{
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
测试:
public class TestPerson {
public static void main(String[] args) throws CloneNotSupportedException {
Person p1 = new Person();
p1.setName("张三");
p1.setAge(10);
Person p2 = p1;
System.out.println("修改前......");
System.out.println(p1);
System.out.println(p2);
System.out.println("修改后");
p2.setName("李四");
System.out.println(p1.getAge());
System.out.println(p2.getAge());
}
}
深拷贝:
public class Person implements Cloneable{
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试:
public class TestPerson {
public static void main(String[] args) throws CloneNotSupportedException {
Person p1 = new Person();
p1.setName("张三");
p1.setAge(10);
Person p2 = (Person) p1.clone();
System.out.println("修改前......");
System.out.println(p1);
System.out.println(p2);
p1.setName("李四");
System.out.println("修改后......");
System.out.println(p1.getName());
System.out.println(p2.getName());
}
}
27、throw和throws的区别
throw表示方法内部抛出某个异常。
throws表示方法上抛出异常。
public class ThrowAndThrows {
public static void main(String[] args) {
throwTest();
throwsTest();
}
public static void throwTest(){
try {
int i = 10/0;
}catch (Exception e){
throw new ArithmeticException("数学运算异常");
}
}
public static void throwsTest() throws ArithmeticException{
int i = 10/0;
}
}
28、final、finally、finalize区别
final表示最终的,当修饰类的时候表示该类不能被继承、修饰方法表示该方法不能被覆盖、修饰变量的时候表示该变量不能被修改,所以需要给定初始值。
finally用于try...catch语句中,通常情况下finally下的语句都会执行。
finalize是Object类中的某个方法,它是用于垃圾回收机制中,如果垃圾回收清理,那么该对象会调用它的finalize方法进行回收。
29、常见的异常类有哪些
30、concurrentMap 和 HashMap 区别
HashMap是线程不安全的、ConcurrentHashMap是线程安全的。
像HashMap是由数组 + 链表(红黑树构成的),如果数组容量不足了,会进行扩容,把原有的key放入新的key中,但是这个时候可能出现线程安全的问题。
ConcurrentHashMap在每一个分段(Segment)上都设置了锁,以保证线程安全,HashMap没有设置锁。