Java封神之路:Java面试备战(十一)

10、注解和反射

10.1 反射的概念与作用

反射的概念:

反射,一种计算机处理方式。是程序可以访问、检测和修改它本身状态或行为的一种能力。

Java反射可以于运行时加载,探知和使用编译期间完全未知的类.

程序在运行状态中, 可以动态加载一个只有名称的类, 对于任意一个已经加载的类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能调用他的任意一个方法和属性;

加载完类之后, 在堆内存中会产生一个Class类型的对象(一个类只有一个Class对象), 这个对象包含了完整的类的结构信息,而且这个Class对象就像一面镜子,透过这个镜子看到类的结构,所以被称之为:反射.

java反射使得我们可以在程序运行时动态加载一个类,动态获取类的基本信息和定义的方法,构造函数,域等。

除了检阅类信息外,还可以动态创建类的实例,执行类实例的方法,获取类实例的域值。反射使java这种静态语言有了动态的特性。

反射的作用:

通过反射可以使程序代码访问装载到JVM 中的类的内部信息

  1. 获取已装载类的属性信息

  2. 获取已装载类的方法

  3. 获取已装载类的构造方法信息

反射的优点:

增加程序的灵活性。

如struts中。请求的派发控制。

当请求来到时。struts通过查询配置文件。找到该请求对应的action。已经方法。

然后通过反射实例化action。并调用响应method。

如果不适用反射,那么你就只能写死到代码里了。

所以说,一个灵活,一个不灵活。

很少情况下是非用反射不可的。大多数情况下反射是为了提高程序的灵活性。因此一般框架中使用较多。因为框架要适用更多的情况。对灵活性要求较高。

10.2 Java反射技术主要实现类有哪些,作用分别是什么

在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中

1)Class类:代表一个类

2)Field 类:代表类的成员变量(属性)

3)Method类:代表类的成员方法

4)Constructor 类:代表类的构造方法

5)Array类:提供了动态创建数组,以及访问数组的元素的静态方法

10.3 Class类的作用?生成Class对象的方法有哪些

Class类是Java 反射机制的起源和入口,用于获取与类相关的各种信息,提供了获取类信息的相关方法。Class类继承自Object类

Class类是所有类的共同的图纸。每个类有自己的对象,好比图纸和实物的关系;每个类也可看做是一个对象,有共同的图纸Class,存放类的 结构信息,能够通过相应方法取出相应信息:类的名字、属性、方法、构造方法、父类和接口

10.4 反射的使用场合和作用、及其优缺点

1)使用场合

在编译时根本无法知道该对象或类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息。

2)主要作用

通过反射可以使程序代码访问装载到JVM 中的类的内部信息,获取已装载类的属性信息,获取已装载类的方法,获取已装载类的构造方法信息

3)反射的优点

反射提高了Java程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类;反射是其它一些常用语言,如C、C++、Fortran 或者Pascal等都不具备的

4) Java反射技术应用领域很广,如软件测试等;许多流行的开源框架例如Struts、Hibernate、Spring在实现过程中都采用了该技术

5)反射的缺点

性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此Java反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。

使用反射会模糊程序内部逻辑:程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。

10.5 Java的反射原理,注解原理?

反射的原理:JVM通过字节码class文件,生成相应的对象。就像正常生成一个对象一样,都是源于字节码class文件,之所以叫反射,只是因为它不像正常的对象声明,如A a=new A()

10.6 class.forName(“java.lang.String”)和String.class.getClassLoader().loadClass()有什么区别

10.7 如何防止单例模式被反射破坏

public class DemoModify {
    
    
    
    private static boolean flag = true;
    
    private  DemoModify() {
    
    
        synchronized (DemoModify.class) {
    
    
            if (flag == false) {
    
    
                flag = !flag;
            }else{
    
    
                throw new RuntimeException("单例模式被侵犯!");
            }
        }
    }
    
    //静态内部类
    private static class SingletonHolder{
    
    
        private static final DemoModify INSTANCE = new DemoModify();
    }
    
    public static DemoModify getInstance(){
    
    
        return SingletonHolder.INSTANCE;
    }
}

10.8 java中的元注解

修饰注解的注解

@Target :定义自定义注解的使用范围
-类
-方法
-属性

@Retention: 定义自定义注解的生效时间
-source 只保留在源文件,编译成class文件后此注解没有
-class 检查性操作,比如@Supperesswaring
-runtime 

@Documented:生成javadoc信息

@Inherited:修饰的自定义注解可以被子类继承 

11、Java中的关键字

11.1 this和super关键字的作用

this是对象内部指代自身的引用,同时也是解决成员变量和局部变量同名问题;this可以调用成员变量,不能调用局部变量;this也可以调用成员方法,但是在普通方法中可以省略this,在构造方法中不允许省略,必须是构造方法的第一条语句。,而且在静态方法当中不允许出现this关键字。

super代表对当前对象的直接父类对象的引用,super可以调用直接父类的成员变量(注意权限修饰符的影响,比如不能访问private成员)

super可以调用直接父类的成员方法(注意权限修饰符的影响,比如不能访问private成员);super可以调用直接父类的构造方法,只限构造方法中使用,且必须是第一条语句。

11.2 static 关键字的作用

static可以修饰变量、方法、代码块和内部类

static属性属于这个类所有,即由该类创建的所有对象共享同一个static属性。可以对象创建后通过对象名.属性名和类名.属性名两种方式来访问。也可以在没有创建任何对象之前通过类名.属性名的方式来访问。

static变量和非static变量的区别(都是成员变量,不是局部变量)

1.在内存中份数不同

不管有多少个对象,static变量只有1份。对于每个对象,实例变量都会有单独的一份

static变量是属于整个类的,也称为类变量。而非静态变量是属于对象的,也称为实例变量

2.在内存中存放的位置不同

2.在内存中存放的位置不同

3.访问的方式不同

实例变量: 对象名.变量名 stu1.name=“小明明”;

静态变量:对象名.变量名 stu1.schoolName=“西二旗小学”; 不推荐如此使用

类名.变量名 Student.schoolName=“东三旗小学”; 推荐使用

4.在内存中分配空间的时间不同

Student.schoolName=“东三旗小学”;或者Student stu1 = new Student(“小明”,“男”,20,98);

static方法也可以通过对象名.方法名和类名.方法名两种方式来访问

static代码块。当类被第一次使用时(可能是调用static属性和方法,或者创建其对象)执行静态代码块,且只被执行一次,主要作用是实现static属性的初始化。

static内部类:属于整个外部类,而不是属于外部类的每个对象。不能访问外部类的非静态成员(变量或者方法),.可以访问外部类的静态成员

11.3 final和abstract关键字的作用

final和abstract是功能相反的两个关键字,可以对比记忆

abstract可以用来修饰类和方法,不能用来修饰属性和构造方法;使用abstract修饰的类是抽象类,需要被继承,使用abstract修饰的方法是抽象方法,需要子类被重写。

final可以用来修饰类、方法和属性,不能修饰构造方法。使用final修饰的类不能被继承,使用final修饰的方法不能被重写,使用final修饰的变量的值不能被修改,所以就成了常量。

*特别注意:*final修饰基本类型变量,其值不能改变,由原来的变量变为常量;但是final修饰引用类型变量,栈内存中的引用不能改变,但是所指向的堆内存中的对象的属性值仍旧可以改变。例如

class Test {
    
    
    public static void main(String[] args) {
    
    
        final Dog dog = new Dog("欧欧");
        dog.name = "美美";//正确
        dog = new Dog("亚亚");//错误
    }
}

11.4 final、finally、finalize的区别

final修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承例如:String类、Math类等。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重写,但是能够重载。 使用final修饰的对象,对象的引用地址不能变,但是对象的值可以变!

finally在异常处理时提供 finally 块来执行任何清除操作。如果有finally的话,则不管是否发生异常,finally语句都会被执行。一般情况下,都把关闭物理连接(IO流、数据库连接、Socket连接)等相关操作,放入到此代码块中。

finalize方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要清理工作。finalize() 方法是在垃圾收集器删除对象之前被调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。 一般情况下,此方法由JVM调用,程序员不要去调用!

11.5 如果变量用final修饰,则怎样?如果方法final修饰,则怎样

1、用final修饰的类不能被扩展,也就是说不可能有子类;

2、用final修饰的方法不能被替换或隐藏:

① 使用final修饰的实例方法在其所属类的子类中不能被替换(overridden);

② 使用final修饰的静态方法在其所属类的子类中不能被重定义(redefined)而隐藏(hidden);

3、用final修饰的变量最多只能赋值一次,在赋值方式上不同类型的变量或稍有不同:

① 静态变量必须明确赋值一次(不能只使用类型缺省值);作为类成员的静态变量,赋值可以在其声明中通过初始化表达式完成,也可以在静态初始化块中进行;作为接口成员的静态变量,赋值只能在其声明中通过初始化表达式完成;

② 实例变量同样必须明确赋值一次(不能只使用类型缺省值);赋值可以在其声明中通过初始化表达式完成,也可以在实例初始化块或构造器中进行;

③ 方法参数变量在方法被调用时创建,同时被初始化为对应实参值,终止于方法体 (body)结束,在此期间其值不能改变;

④ 构造器参数变量在构造器被调用(通过实例创建表达式或显示的构造器调用)时创建,同时被初始化,为对应实参值,终止于构造器体结束,在此期间其值不能改变;

⑤ 异常处理器参数变量在有异常被try语句的catch子句捕捉到时创建,同时被初始化为实际的异常对象,终止于catch语句块结束,在此期间其值不能改变;

⑥ 局部变量在其值被访问之前必须被明确赋值;

猜你喜欢

转载自blog.csdn.net/weixin_54707168/article/details/113976596