Java面试题03(每次10道)

177 阅读6分钟

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没有设置锁。