动态代理中变量为何final(其实是局部内部类的原因)

博客围绕动态代理中变量需为final这一问题展开,指出原因在于内部内部类。通过对该现象的探讨,有助于理解动态代理和内部类的相关原理,对信息技术领域的开发人员有一定参考价值。
http://feiyeguohai.iteye.com/blog/1500108
<think> 我们继续围绕Java的动态代理、基础语法、类与对象等知识点设计20道选择题,包含单选和多选,并增加少量深度和复杂度较高的题目。题目将涵盖代理模式、反射、类加载、对象生命周期、内部类、枚举、注解等进阶内容。 ### Java选择题集(动态代理、基础语法、类与对象) 1. **(单选)** Java动态代理主要基于以下哪个接口实现? A. `InvocationHandler` B. `Proxy` C. `Runnable` D. `Serializable` **答案:** A **解答:** 动态代理的核心是`InvocationHandler`接口,它定义了`invoke`方法,用于处理代理对象上的方法调用。`Proxy`类用于创建代理对象,但不是接口。 2. **(多选)** 以下关于Java动态代理的说法,哪些是正确的? A. 动态代理只能代理接口 B. 动态代理可以代理普通类 C. 动态代理在运行时生成代理类 D. 动态代理比静态代理更灵活 **答案:** A、C、D **解答:** Java标准动态代理(基于`Proxy`)只能代理接口(A正确)。若要代理类,可以使用CGLIB等第三方库(B错误)。动态代理在运行时通过反射生成代理类(C正确),并且无需为每个被代理对象编写代理类,更灵活(D正确)。 3. **(单选)** 以下代码执行后,输出是什么? ```java public class Test { public static void main(String[] args) { String s1 = "hello"; String s2 = new String("hello"); System.out.println(s1 == s2); System.out.println(s1.equals(s2)); } } ``` A. true, true B. false, true C. true, false D. false, false **答案:** B **解答:** `s1`指向字符串常量池中的"hello",`s2`在堆中新建对象。`==`比较引用地址,不同(false);`equals()`比较字符串内容,相同(true)。 4. **(单选)** 在Java中,以下哪个关键字用于定义抽象类? A. `interface` B. `abstract` C. `class` D. `implements` **答案:** B **解答:** 抽象类使用`abstract class`声明。`interface`定义接口,`class`定义普通类,`implements`用于实现接口。 5. **(多选)** 关于内部类,以下说法正确的是: A. 静态内部类不能访问外部类的非静态成员 B. 局部内部类可以访问所在方法的局部变量,但该变量必须是final或等效final C. 匿名内部类不能有构造器 D. 成员内部类不能是静态的 **答案:** A、B、C **解答:** 静态内部类(static nested class)不持有外部类引用,故不能访问非静态成员(A正确)。局部内部类访问的局部变量必须final或等效final(B正确)。匿名内部类没有显式类名,因此不能定义构造器(C正确)。成员内部类可以是静态的(即静态内部类),D错误。 6. **(单选)** 以下代码中,输出结果是什么? ```java public class Outer { private int value = 10; class Inner { private int value = 20; public void print() { int value = 30; System.out.println(value); System.out.println(this.value); System.out.println(Outer.this.value); } } public static void main(String[] args) { new Outer().new Inner().print(); } } ``` A. 30, 20, 10 B. 30, 10, 20 C. 10, 20, 30 D. 编译错误 **答案:** A **解答:** 内部类方法中,`value`(30)是局部变量,`this.value`(20)是内部类实例变量,`Outer.this.value`(10)是外部类实例变量。 7. **(单选)** 以下关于枚举(enum)的说法,错误的是: A. 枚举可以定义构造方法 B. 枚举可以继承其他类 C. 枚举可以实现接口 D. 枚举不能继承自枚举 **答案:** B **解答:** 枚举在Java中是隐式继承`java.lang.Enum`的,因此不能再继承其他类(B错误)。枚举可以有构造方法、实现接口,并且不能继承其他枚举(枚举类都是final的)。 8. **(多选)** 以下哪些注解是Java内置的注解? A. `@Override` B. `@Deprecated` C. `@SuppressWarnings` D. `@FunctionalInterface` **答案:** A、B、C、D **解答:** 以上都是Java内置注解:`@Override`标记重写方法,`@Deprecated`标记过时,`@SuppressWarnings`抑制警告,`@FunctionalInterface`标记函数式接口。 9. **(单选)** 使用反射获取一个类的构造方法,应调用Class类的哪个方法? A. `getMethod()` B. `getConstructor()` C. `getField()` D. `getDeclaredMethods()` **答案:** B **解答:** `getConstructor()`用于获取公开的构造方法。`getMethod()`获取公开方法(非构造),`getField()`获取字段,`getDeclaredMethods()`获取本类声明的所有方法(包括私有,但不包括构造)。 10. **(单选)** 以下代码执行后,输出是什么? ```java public class Test { public static void main(String[] args) { Integer a = 100; Integer b = 100; Integer c = 200; Integer d = 200; System.out.println(a == b); System.out.println(c == d); } } ``` A. true, true B. false, false C. true, false D. false, true **答案:** C **解答:** Java对Integer缓存了-128~127的值,因此100在缓存范围内,a和b指向同一个对象(true);200超出范围,c和d是新对象(false)。 11. **(多选)** 关于Java类加载机制,以下说法正确的是: A. 类加载过程包括:加载、连接(验证、准备、解析)、初始化 B. 类加载器有:Bootstrap、Extension、Application C. 双亲委派模型要求类加载器先让父类加载器尝试加载 D. 同一个类被不同类加载器加载后,在JVM中是同一个类 **答案:** A、B、C **解答:** D错误:同一个类被不同类加载器加载后,在JVM中视为不同类(类型不同)。A、B、C均为类加载机制的正确描述。 12. **(单选)** 以下关于Java对象的初始化顺序,正确的是: A. 静态块 -> 实例块 -> 构造器 B. 实例块 -> 静态块 -> 构造器 C. 构造器 -> 静态块 -> 实例块 D. 静态块 -> 构造器 -> 实例块 **答案:** A **解答:** 类初始化:父类静态块 -> 子类静态块;实例初始化:父类实例块 -> 父类构造器 -> 子类实例块 -> 子类构造器。因此静态块最先,然后实例块,最后构造器。 13. **(单选)** 以下代码执行后,输出是什么? ```java public class Test { public static void main(String[] args) { System.out.println(test()); } public static int test() { try { return 1; } catch (Exception e) { return 2; } finally { return 3; } } } ``` A. 1 B. 2 C. 3 D. 编译错误 **答案:** C **解答:** finally块中的return会覆盖try块中的return,因此输出3。 14. **(多选)** 以下哪些方法可以用于获取Class对象? A. `Class.forName("类名")` B. `对象.getClass()` C. `类名.class` D. `ClassLoader.loadClass("类名")` **答案:** A、B、C、D **解答:** 以上都是获取Class对象的方式:A通过Class的静态方法,B通过对象的getClass()方法,C通过类字面常量,D通过类加载器。 15. **(单选)** 在动态代理中,`Proxy.newProxyInstance()`方法的参数不包括: A. 类加载器 B. 代理类实现的接口列表 C. InvocationHandler实例 D. 被代理类对象 **答案:** D **解答:** `newProxyInstance(ClassLoader, Class<?>[], InvocationHandler)`参数为类加载器、接口数组、调用处理器。不需要被代理类对象,因为动态代理基于接口。 16. **(单选)** 以下关于Java中对象生命周期的说法,错误的是: A. 对象通过new关键字创建 B. 对象不再被引用时,会被垃圾回收 C. finalize()方法在对象被回收前一定会被调用 D. 对象可达性分析是垃圾回收的常用算法 **答案:** C **解答:** finalize()方法不保证一定会执行(如JVM退出时),且已被标记为过时,因此C错误。 17. **(多选)** 以下哪些是合法的Java标识符? A. `_name` B. `$value` C. `2var` D. `class` **答案:** A、B **解答:** 标识符以字母、下划线、$开头,不能以数字开头(C错误)。`class`是关键字(D错误)。 18. **(单选)** 以下代码执行后,输出是什么? ```java public class Test { public static void main(String[] args) { int a = 5; int b = a++; int c = ++a; System.out.println(a + "," + b + "," + c); } } ``` A. 6,5,6 B. 7,5,7 C. 7,5,6 D. 6,5,7 **答案:** B **解答:** `a++`先赋值后自增:b=5, a变为6;`++a`先自增后赋值:a先变为7,c=7。因此a=7, b=5, c=7。 19. **(单选)** 以下关于Java中方法重载(Overload)的说法,错误的是: A. 方法名必须相同 B. 参数列表必须不同(类型、个数、顺序) C. 返回类型可以不同 D. 访问修饰符可以不同 **答案:** C **解答:** 方法重载要求方法名相同,参数列表不同,与返回类型和访问修饰符无关。但仅返回类型不同不能构成重载(编译错误)。 20. **(多选)** 以下关于Java中反射操作的说法,哪些是正确的? A. 反射可以调用私有方法 B. 反射会破坏封装性 C. 反射性能通常低于直接调用 D. 反射无法获取泛型类型信息 **答案:** A、B、C **解答:** 反射通过`setAccessible(true)`可以调用私有方法(A正确),但破坏了封装(B正确)。反射涉及动态解析,性能较低(C正确)。反射可以通过`Type`相关接口获取泛型信息(D错误)。 §§相关问题§§ 1. Java中的动态代理与CGLIB代理有何区别? 2. 如何自定义类加载器? 3. 内部类持有外部类引用会导致内存泄漏吗?如何避免? 4. 枚举类型在JVM中是如何实现的? 5. 反射在框架(如Spring)中的应用有哪些? </think>### Java选择题集(动态代理、基础语法、类与对象) 以下题目基于Java的动态代理、基础语法、类与对象等知识点设计,包含单选和多选(标注),涵盖核心概念和进阶应用。每题后附详细解答。 --- 1. **(单选)** Java动态代理的核心接口是? A. `InvocationHandler` B. `Proxy` C. `Runnable` D. `Callable` **答案:** A **解答:** `InvocationHandler` 是动态代理的核心接口,其 `invoke()` 方法实现代理逻辑。`Proxy` 是用于创建代理对象的工具类。 2. **(多选)** 以下关于动态代理的说法正确的是? A. 只能代理接口 B. 代理类在运行时生成 C. JDK动态代理基于反射实现 D. 可代理静态方法 **答案:** A、B、C **解答:** JDK动态代理仅支持接口代理(A),运行时生成代理类字节码(B),基于反射机制(C)。无法代理静态方法(D错误)。 3. **(单选)** 以下代码的输出是? ```java public class Test { public static void main(String[] args) { int a = 5; System.out.println(a++ + ++a); } } ``` A. 10 B. 11 C. 12 D. 13 **答案:** C **解答:** `a++` 返回5(a变为6),`++a` 返回7(a先增为7),5+7=12。 4. **(单选)** 以下关于类初始化的顺序正确的是? A. 静态代码块 → 实例代码块 → 构造器 B. 实例代码块 → 静态代码块 → 构造器 C. 构造器 → 静态代码块 → 实例代码块 D. 静态代码块 → 构造器 → 实例代码块 **答案:** A **解答:** 类初始化顺序:父类静态块 → 子类静态块 → 父类实例块 → 父类构造器 → 子类实例块 → 子类构造器。 5. **(多选)** 以下哪些属于引用类型? A. `String` B. `int[]` C. `Integer` D. `void` **答案:** A、B、C **解答:** 引用类型包括类(A、C)、数组(B)。`void` 是基本返回类型占位符。 6. **(单选)** 使用动态代理创建对象的方法是? A. `Proxy.newProxyInstance()` B. `InvocationHandler.createProxy()` C. `Class.newProxyClass()` D. `Reflection.buildProxy()` **答案:** A **解答:** `Proxy.newProxyInstance(ClassLoader, Class[], InvocationHandler)` 是标准创建动态代理对象的方法。 7. **(多选)** 以下关于`final`的说法正确的是? A. final类不可被继承 B. final方法不可被重写 C. final变量初始化后不可修改 D. final参数在方法内可重新赋值 **答案:** A、B、C **解答:** final类禁止继承,final方法禁止重写,final变量(常量)不可修改。final参数在方法内不可重新赋值(D错误)。 8. **(单选)** 以下代码的输出是? ```java public class Test { static { System.out.print("A"); } public Test() { System.out.print("B"); } public static void main(String[] args) { new Test(); } } ``` A. A B. B C. AB D. BA **答案:** C **解答:** 静态块(A)在类加载时执行,构造器(B)在对象实例化时执行。 9. **(单选)** 动态代理的典型应用场景是? A. 数据库连接管理 B. AOP切面编程 C. 多线程同步 D. 异常处理链 **答案:** B **解答:** 动态代理广泛应用于AOP(如Spring的拦截器),实现日志、事务等横切关注点。 10. **(多选)** 以下哪些方法会触发类加载? A. 创建类的实例 B. 调用类的静态方法 C. 访问类的静态字段 D. 使用`Class.forName()` **答案:** A、B、C、D **解答:** 以上操作均会触发类加载(若类尚未加载)。 11. **(单选)** 以下关于内部类的说法错误的是? A. 成员内部类可访问外部类私有成员 B. 静态内部类不持有外部类引用 C. 局部内部类不能访问方法局部变量 D. 匿名内部类可实现接口 **答案:** C **解答:** 局部内部类可访问final或等效final局部变量(C错误)。其他选项正确。 12. **(单选)** 动态代理中,`invoke()`方法的第一个参数是? A. 被代理的对象 B. 被调用的方法 C. 方法参数数组 D. 代理对象本身 **答案:** D **解答:** `invoke(Object proxy, Method method, Object[] args)` 的第一个参数是代理对象自身。 13. **(多选)** 以下对象创建方式会调用构造器的是? A. `new`关键字 B. 反序列化 C. 反射`Class.newInstance()` D. 对象克隆 **答案:** A、C **解答:** `new`和反射会调用构造器。反序列化和克隆不调用构造器(B、D错误)。 14. **(单选)** 以下代码的输出是? ```java public class Test { public static void main(String[] args) { String s1 = "abc"; String s2 = new String("abc"); System.out.println(s1 == s2.intern()); } } ``` A. true B. false C. 编译错误 D. 运行时异常 **答案:** A **解答:** `s2.intern()` 返回常量池中的"abc",与`s1`指向同一对象(true)。 15. **(单选)** 以下关于枚举的说法正确的是? A. 枚举本质是`final`类 B. 枚举构造器只能是`private` C. 枚举可实现接口 D. 枚举可继承其他类 **答案:** B、C **解答:** 枚举构造器默认为private(B正确),可实现接口(C正确)。枚举隐式继承`java.lang.Enum`(A、D错误)。 16. **(单选)** 动态代理的局限性是? A. 只能代理public方法 B. 无法代理final方法 C. 性能低于静态代理 D. 不支持异常处理 **答案:** B **解答:** final方法无法被代理(B正确)。可代理非public方法(A错误),支持异常处理(D错误),性能问题可通过缓存优化(C不准确)。 17. **(多选)** 以下会导致`NullPointerException`的是? A. `int[] arr = null; arr[0] = 1;` B. `String s = null; s.length();` C. `Object o = null; o.getClass();` D. `Integer i = null; int j = i;` **答案:** A、B、D **解答:** A(数组空指针)、B(调用空对象方法)、D(自动拆箱空对象)会抛NPE。C返回`null`的Class对象(实际抛出`NullPointerException`,因为`getClass()`是实例方法)。 18. **(单选)** 以下代码的输出是? ```java public class Test { public static void main(String[] args) { System.out.println(test()); } static int test() { try { return 1; } finally { return 2; } } } ``` A. 1 B. 2 C. 先输出1后输出2 D. 编译错误 **答案:** B **解答:** finally中的return会覆盖try中的返回值。 19. **(多选)** 以下关于对象相等的说法正确的是? A. `==`比较对象内存地址 B. `equals()`默认与`==`相同 C. 重写`equals()`需同时重写`hashCode()` D. 两个对象`hashCode()`相同则`equals()`必为true **答案:** A、B、C **解答:** `==`比较地址(A),Object的`equals()`即`==`(B),重写`equals()`时必须重写`hashCode()`(C)。hashCode相同不保证equals为true(D错误)。 20. **(单选)** 以下实现动态代理的代码空白处应填? ```java public class DynamicProxyDemo { interface Service { void execute(); } static class RealService implements Service { public void execute() { System.out.println("Real execution"); } } public static void main(String[] args) { RealService real = new RealService(); Service proxy = (Service) Proxy.newProxyInstance( Service.class.getClassLoader(), new Class[]{Service.class}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before"); Object result = ________; // 空白处 System.out.println("After"); return result; } } ); proxy.execute(); } } ``` A. `method.invoke(proxy, args)` B. `method.invoke(real, args)` C. `real.execute()` D. `Service.execute()` **答案:** B **解答:** 需调用真实对象`real`的方法,使用`method.invoke(real, args)`。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值