Java 面试题:Java 的动态代理是基于什么原理?

编程语言通常有各种不同的分类角度,动态类型和静态类型就是其中一种分类角度,简单区分就是语言类型信息是在运行时检查,还是编译期检查。

与其近似的还有一个对比,就是所谓强类型和弱类型,就是不同类型变量赋值时,是否需要显式地(强制)进行类型转换。那么,如何分类 Java 语言呢?

通常认为,Java 是静态的强类型语言,但是因为提供了类似反射等机制,也具备了部分动态类型语言的能力。



1、面试问题

今天的面试问题:Java 的动态代理是基于什么原理?


2、问题分析

这个面试题主要考察了以下几个关键点:

  1. 反射机制的理解:这个问题测试你是否了解 Java 中反射机制的基本概念,特别是如何在运行时动态创建代理类和对象。反射是动态代理实现的核心,允许程序在运行时查询对象的属性和调用对象的方法;
  2. 接口和代理设计模式的应用:通过动态代理的问题,面试官希望看到你是否理解代理设计模式的基本原理以及如何通过接口将代理类与实际执行类解耦,增强功能或进行访问控制;
  3. 适用场景的理解:面试官通过这个问题可能会进一步探讨你对动态代理应用场景的理解,比如在哪些情况下使用动态代理,它在 AOP(面向切面编程)、事务管理、日志记录等方面的应用。
  4. Java API 的熟悉程度:了解 java.lang.reflect.Proxy 和 ;java.lang.reflect.InvocationHandler 等关键类的使用方法和特性,显示出你对 Java API 的掌握程度。这是评估 Java 开发者技能的一个重要方面。

总体来说,这个问题不仅考察了技术细节,还考察了面试者在面对设计模式和架构选择时的决策能力。理解和应用动态代理可以帮助开发者在需要时实现代码的解耦和功能增强,是高级 Java 开发者的标志性技能之一。


3、典型回答

首先,Java 的动态代理是基于反射机制实现的高级功能,它允许我们在运行时动态创建代理类和对象,来代理实际对象,进行方法调用的中介处理。这一机制通过 Java 核心的反射 API 中的 Proxy 类和 InvocationHandler 接口实现。

其中实现的具体过程是:通过一个类加载器、一组接口及一个调用处理器作为输入参数,利用Proxy类的 newProxyInstance 方法在内存中动态创建一个实现指定接口的代理对象。该代理对象会将所有方法调用转发到实现了 InvocationHandler 接口的调用处理器的 invoke 方法中。

invoke 方法里,可以自定义方法调用前后的操作,如日志记录、权限检查和事务处理等。这使得动态代理不仅增强了程序的灵活性,而且有助于业务逻辑与系统服务的解耦。

此外,除了 JDK 内置的动态代理实现,还可以使用字节码操作技术如 ASM、CGLIB(基于ASM)和Javassist 等。这些技术提供了更底层的操作能力,比如允许直接针对类而非仅限于接口创建代理,适用于性能要求更高或需求更复杂的系统。


4、问题深入

如果继续深入,面试官可以从各种不同的角度考察,比如可以:

  • 原理的深入理解,比如:①、解释InvocationHandlerProxy类的内部工作机制;②、讨论 Java 反射机制在动态代理中的作用及其性能影响;
  • 性能优化,比如:①、讨论动态代理的性能开销及如何减少这些开销;②、讨比较JDK动态代理与其他代理技术(如CGLIB)在性能方面的差异。
  • 实际应用场景,比如:①、讨论动态代理在实际开发中的具体应用,如在哪些场景下是不可替代的;②、探讨如何使用动态代理进行错误处理和异常管理。

5、问题拓展

5.1、问题拓展:解释InvocationHandlerProxy类的内部工作机制;

Java 的 InvocationHandlerProxy 类是实现动态代理的关键组件,广泛应用于运行时动态创建代理类的情况,如远程方法调用、事务管理、日志记录等。这种机制主要依赖 Java 反射 API,使得开发者能够在运行时动态处理方法调用。

5.1.1、Proxy

Proxy 类提供了用于创建动态代理实例的静态方法。动态代理类是在运行时生成的类,它实现了指定的一组接口。

  • 创建动态代理实例

    :最常用的方法是

    Proxy.newProxyInstance()
    

    ,这个方法需要三个参数:

    1. 类加载器(ClassLoader):用来定义代理类的类加载器。
    2. 接口数组(Class[]):这是一个接口数组,代理类将实现这些接口。
    3. InvocationHandler 实现:当代理实例的方法被调用时,将会调用此处理器的 invoke() 方法。
5.1.2、InvocationHandler 接口

InvocationHandler 是一个接口,开发者需要实现它的 invoke() 方法来定义代理实例的行为。

  • invoke() 方法

    :这个方法有三个参数:

    1. 代理实例(proxy):调用该方法的代理实例。
    2. 方法对象(Method):对应于在代理实例上调用的接口方法的 Method 实例。
    3. 参数数组(args):调用接口方法时传递的参数。
5.1.3、工作机制

当代理实例的方法被调用时,调用将被重定向到 InvocationHandlerinvoke() 方法。invoke() 方法可以根据需要进行增强处理,如日志记录、事务处理、延迟加载等,然后可能通过反射调用实际对象的相应方法。

例如,如果你有一个接口 MyInterface 和实现该接口的类 MyImpl,你可以创建一个 InvocationHandler,在调用任何 MyInterface 的方法之前和之后执行特定操作。创建动态代理后,任何对 MyInterface 方法的调用都会转发给 InvocationHandler

示例代码:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface MyInterface {
    void doSomething();
}

class MyImpl implements MyInterface {
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

class MyInvocationHandler implements InvocationHandler {
    private final MyInterface original;

    public MyInvocationHandler(MyInterface original) {
        this.original = original;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method call");
        Object result = method.invoke(original, args);  // 调用实际对象的方法
        System.out.println("After method call");
        return result;
    }
}

public class ProxyExample {
    public static void main(String[] args) {
        MyInterface original = new MyImpl();
        MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
            MyInterface.class.getClassLoader(),
            new Class<?>[] {MyInterface.class},
            new MyInvocationHandler(original)
        );

        proxy.doSomething();  // 调用代理的方法,这将触发 MyInvocationHandler 的 invoke 方法
    }
}

这个例子中:

  • MyInterface 是一个接口,MyImpl 是这个接口的一个具体实现;
  • MyInvocationHandler 实现了 InvocationHandler 接口,用于定义在调用方法前后要执行的操作;
  • 使用 Proxy.newProxyInstance() 创建了一个 MyInterface 的代理实例。当调用 proxy.doSomething() 时,实际上会通过 MyInvocationHandlerinvoke() 方法进行调用。
5.2、问题拓展:论 Java 反射机制在动态代理中的作用及其性能影响以及及如何减少这些开销;

Java 反射机制在动态代理中的作用是核心的,但同时也会对性能产生一定的影响。下面将详细讨论这两个方面:

5.2.1、反射在动态代理中的作用
  1. 接口方法的动态调用:反射允许动态代理在运行时查找并调用方法。当代理类的一个方法被调用时,InvocationHandlerinvoke 方法就会被触发,这个方法利用反射来决定哪一个方法应该被调用,以及如何传递参数。

  2. 无需硬编码:使用反射,开发者无需在编写代码时就确定要调用哪些类和方法。这使得代码更加灵活和可扩展,因为可以在运行时动态加载和调用类和方法。

  3. 适用性广泛:反射使得动态代理不仅可以用于实现已知的接口,还可以动态地应对程序扩展中新增的接口,提高了代码的复用性和模块间的解耦能力。

5.2.2、性能影响

尽管反射在动态代理中提供了极大的灵活性和强大的功能,它也有一些性能上的不足:

  1. 性能开销:反射操作本质上比直接方法调用更慢。这是因为反射涉及到类型检查和方法调用解析等额外的处理步骤。每次通过反射调用方法时,JVM 都需要检查方法的访问权限,并且在调用前将参数类型匹配到方法的参数类型;

  2. 内存消耗:使用反射的动态代理可能会额外消耗更多内存,因为 JVM 需要为被代理的方法以及相关的数据结构(如 Method 对象)分配内存;

  3. 优化限制:使用反射调用方法通常不能享受到 JIT 编译器的一些优化,如内联展开等,这进一步影响了性能。

5.2.3、性能优化策略

尽管反射带来了性能挑战,但可以采取一些措施来缓解这些问题:

  • 缓存反射对象:可以缓存关键的反射对象(如 Method 实例),以减少查找和创建这些对象的开销;
  • 限制代理类数量:避免过度使用动态代理,特别是在性能敏感的应用场景中,因为每一个代理类都会增加额外的运行时开销;
  • 使用第三方库:例如 CGLib 或 ByteBuddy 等库可以通过生成字节码来创建代理,而不是使用 Java 原生的反射,这样可以提高性能。

总之,虽然反射和动态代理提供了极大的灵活性和强大的功能,但在使用时需要权衡其带来的性能开销。在设计系统时,合理选择使用动态代理的场景,并采取适当的优化措施,是提高系统性能的关键。


5.3、问题拓展:讨比较JDK动态代理与其他代理技术(如CGLIB)在性能方面的差异。

在 Java 中,动态代理主要有两种常见的实现方式:JDK 动态代理和第三方库,如 CGLib。这两种技术在性能和适用性方面都有各自的特点和差异。

5.3.1、JDK 动态代理

JDK 动态代理使用 Java 自带的代理类库来实现,它依赖于反射(java.lang.reflect 包)和接口:

  • 基于接口:JDK 动态代理只能代理接口,不支持类。这意味着只有实现了接口的类才可以被代理;
  • 性能影响:由于 JDK 代理基于反射实现,其性能通常不如直接方法调用。尽管 Java 的反射性能在近年有所改进,但反射调用仍然比直接调用慢;
  • 内存和加载时间:JDK 代理的加载时间相对较短,内存消耗较少,因为它只需要生成实现指定接口的代理类。
5.3.2、CGLib

CGLib(Code Generation Library)是一个强大的、高性能的代码生成库,它使用字节码增强技术(通过继承类)来实现代理,而不仅仅是基于接口:

  • 基于类的代理:CGLib 可以代理没有实现接口的类,因此它比 JDK 动态代理更为灵活;
  • 性能优势:CGLib 在运行时生成的是具体类的子类,并覆盖其中的方法。相比于 JDK 的反射机制,CGLib 通常能提供更好的性能,因为它直接操作字节码,避免了反射调用的开销;
  • 内存和加载时间:CGLib 生成的代理类比 JDK 代理更复杂,可能会导致更长的加载时间和更高的内存消耗。此外,CGLib 在生成代理类时需要处理更多的字节码操作,这在初次加载时可能导致性能开销。
5.3.3、性能比较
  1. 调用速度:CGLib 代理的方法调用通常比 JDK 代理快,因为它避免了使用反射。CGLib 直接修改字节码,减少了调用时的计算量;
  2. 启动速度:JDK 动态代理在生成代理类时较快,因为它仅涉及到简单的接口实现。而 CGLib 在启动时可能需要更多时间来生成字节码;
  3. 资源消耗:CGLib 由于在运行时创建较复杂的类,可能会消耗更多内存。JDK 动态代理则相对节省资源。
5.3.4、应用场景

如果你的类已经实现了接口,或者你只需要代理接口方法,JDK 动态代理是一个简单而高效的选择;

如果你需要代理没有实现接口的类,或者追求最佳性能,CGLib 是更好的选择。

总的来说,选择哪种代理技术取决于具体需求、性能考量和代理对象的特点。在不需要处理接口的情况下,CGLib 的性能优势通常会使它成为更受欢迎的选择。

5.4、问题拓展:讨论动态代理在实际开发中的具体应用,如在哪些场景下是不可替代的

动态代理在实际开发中扮演了非常重要的角色,尤其是在需要增强或修改原有类行为而不改变原有代码的情况下。以下是一些具体的应用场景,展示了动态代理的价值和不可替代性:

5.4.1、AOP(面向切面编程)

AOP 是动态代理应用最广泛的领域之一,尤其在如 Spring 框架中。通过动态代理,可以在不修改原有代码的基础上,为方法调用提供横切关注点(如日志记录、事务管理、安全检查等)的处理。这种方式使得关注点分离,增强了代码的可维护性和复用性。

5.4.2、事务管理

在企业应用中,事务管理是必不可少的功能,特别是在数据库操作中。通过使用动态代理,可以自动地为业务方法调用加入事务处理逻辑,如开启事务、提交或回滚事务。这样做可以避免将事务管理代码硬编码到业务逻辑中,提高代码的清晰性和可维护性。

5.4.3、远程方法调用(RMI)

动态代理常用于实现远程方法调用的客户端逻辑。在 Java RMI 或其他远程调用框架如 Spring 的 Remoting 中,客户端可以通过动态代理透明地调用远程服务。代理对象负责将本地接口调用转换为网络请求,并处理来自服务端的响应,对用户来说,远程调用与本地调用无异。

5.4.4、资源管理

动态代理可以用于智能资源管理,如数据库连接和线程池的管理。代理可以控制资源的分配和释放,确保例如数据库连接始终在使用完毕后被正确关闭,从而避免资源泄露。

5.4.5、接口兼容性和适配器

在需要适配旧版本接口到新的实现时,动态代理可以充当适配器的角色,无需修改现有代码。这种方式非常适合在升级和维护大型遗留系统时,为旧接口提供新的实现。

5.4.6、延迟加载

动态代理可以用于实现延迟加载(懒加载),特别是在处理大型对象或复杂对象关系时。例如,代理可以控制只有在实际需要时才加载对象的某些部分,从而减少系统的初始化时间和运行时的内存消耗。

5.4.7、安全访问控制

动态代理还可以用于增强安全性,通过代理控制对敏感方法的访问。代理可以在方法执行前进行安全检查,如验证用户权限,确保只有具备相应权限的用户才能执行某些操作。

这些应用场景展示了动态代理在解耦、增强方法、处理交叉关注点等方面的巨大优势。在现代软件开发中,动态代理技术是实现中间件、框架和各种库的一个不可或缺的工具,它提供了一种强大且灵活的方法来动态修改和增强对象的行为。

5.5、问题拓展;探讨如何使用动态代理进行错误处理和异常管理

使用动态代理进行错误处理和异常管理是一种增强应用程序健壮性的有效技术。通过动态代理,开发者可以在不修改原有业务逻辑代码的情况下,集中处理方法调用过程中可能发生的异常。这种模式在企业级应用中非常常见,特别是在需要统一异常处理逻辑的系统中。

5.5.1、实现动态代理的异常管理

动态代理允许开发者在一个集中的位置拦截方法调用,并在这个层面上实施异常处理策略。以下是实现此功能的基本步骤:

  1. 定义业务接口:首先定义一个或多个业务逻辑接口,这些接口包含了应用程序中需要执行的方法;

  2. 实现业务逻辑:实现这些接口,编写具体的业务逻辑;

  3. 创建动态代理:使用 JDK 动态代理或第三方库(如 CGLib),创建一个代理类,该类在内部使用 InvocationHandler

  4. 编写 InvocationHandler 实现:在 InvocationHandlerinvoke 方法中,实现异常捕获和处理逻辑。这允许你在方法执行前后添加额外的处理,比如异常日志记录、异常转换、重试逻辑等;

  5. 使用代理实例:在应用程序中,使用动态代理实例代替原始业务对象。所有对原始对象的方法调用都会通过代理,并由代理中的异常处理逻辑处理。

示例代码:

以下是使用 JDK 动态代理进行异常管理的简单示例:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Service {
    void process() throws Exception;
}

class ServiceImpl implements Service {
    public void process() throws Exception {
        // 模拟业务逻辑
        System.out.println("Processing service");
        throw new Exception("Something went wrong!");
    }
}

class ExceptionHandlingInvocationHandler implements InvocationHandler {
    private Object target;

    public ExceptionHandlingInvocationHandler(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            return method.invoke(target, args);
        } catch (Exception e) {
            handleException(e);
            return null;  // 根据具体需求返回适当的值或抛出自定义异常
        }
    }

    private void handleException(Exception e) {
        // 统一异常处理逻辑
        System.out.println("Handled exception: " + e.getMessage());
    }
}

public class ProxyExample {
    public static void main(String[] args) {
        Service realService = new ServiceImpl();
        Service proxyService = (Service) Proxy.newProxyInstance(
            Service.class.getClassLoader(),
            new Class<?>[]{Service.class},
            new ExceptionHandlingInvocationHandler(realService)
        );

        try {
            proxyService.process();
        } catch (Exception e) {
            System.out.println("Exception: " + e.getMessage());
        }
    }
}
5.5.2、动态代理的异常处理优势
  • 集中管理:可以在一个地方集中管理所有异常处理逻辑,提高代码的可维护性;
  • 解耦:将异常处理逻辑从业务代码中解耦,使业务逻辑更加清晰;
  • 灵活性:可以灵活地添加或修改异常处理策略而不需要修改业务逻辑代码;
  • 重用性:相同的异常处理逻辑可以应用到多个服务或组件上。

动态代理提供了一种强大的方式来增强方法调用,包括统一的异常处理,这在构建大型、复杂的系统时尤为重要。

  • 115
    点赞
  • 104
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 132
    评论
超级有影响力的Java面试题大全文档 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。抽象包括两个方面,一是过程抽象,二是数据抽象。 2.继承:  继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。 3.封装:  封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。 4. 多态性:  多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。 5、String是最基本的数据类型吗?  基本数据类型包括byte、int、char、long、float、double、boolean和short。  java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类 6、int 和 Integer 有什么区别  Java 提供两种不同的类型:引用类型和原始类型(或内置类型)。Int是java的原始数据类型,Integer是java为int提供的封装类。Java为每个原始类型提供了封装类。 原始类型 封装类 boolean Boolean char Character byte Byte short Short int Integer long Long float Float double Double  引用类型和原始类型的行为完全不同,并且它们具有不同的语义。引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题,这种类型以哪种类型的数据结构存储,当引用类型和原始类型用作某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为 null,而原始类型实例变量的缺省值与它们的类型有关。 7、String 和StringBuffer的区别  JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要改变的时候你就可以使用StringBuffer。典型地,你可以使用 StringBuffers来动态构造字符数据。 8、运行时异常与一般异常有何异同?  异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。 9、说出Servlet的生命周期,并说出Servlet和CGI的区别。  Servlet被服务器实例化后,容器运行其init方法,请求到达时运行其service方法,service方法自动派遣运行与请求对应的doXXX方法(doGet,doPost)等,当服务器决定将实例销毁的时候调用其destroy方法。 与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。 10、说出ArrayList,Vector, LinkedList的存储性能和特性  ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。 11、EJB是基于哪些技术实现的?并说出SessionBean和EntityBean的区别,StatefulBean和StatelessBean的区别。 EJB包括Ses
int和Integer有什么区别? 答:int是java的原始数据类型,Integer是java为int提供的封装类,java为每个原始数据类型都提供了封装类。 String和StringBuffer的区别? 答:String是不可变的对象,每次对String类型进行改变都相当于产生了一个新的对象,StringBuffer是可变的字符序列,所以如果要经常改变某个字符串的话建议使用StringBuffer。 list、set、map问题? 答:set 不允许重复,无序 list 允许重复,有序 map 成对的数据结构,键值必须具有唯一性 Servlet和CGI的区别? 答:Servlet与CGI的区别在于Servlet处于服务器进程中,它通过多线程方式允许其service方法,一个实例可以服务于多个请求,并且其实例一般不会被销毁,而CGI对每个请求都产生新的进程,服务完后就销毁,所以效率上低于Servlet。 面向对象的特征? 答:1:封装:通过定义类并且给类的属性和方法加上访问控制 2:继承:子类继承父类,子类可以拥有父类中已定义的方法,并且子类可以修改父类中的方法使其更适合特殊需求。 3:多台:不同对象对统一消息作出不同响应称之为多态 4:抽象:忽略与当前主题无关的那些方面,将注意力集中在与当前目标有关的方面 运行时异常和一般异常有何异同? 答:运行时异常时(JVM)java虚拟机在运行过程中发生的问题,比如:内存溢出等问题。这类异常没法要求程序员去一一捕获并抛出,一般异常是Java类库或程序员自己写的代码发生的错误,这类异常可以由我们去一一捕获并抛出。 多线程几种实现方法,同步? 答:多线程有两种实现方法,一种是继承Thread类或者实现Runnable接口。同步就是在方法返回类型后面加上synchronized。 c#中的委托,事件是不是委托? 答:委托就是将方法作为一个参数带入另一个方法叫做委托,事件是一种特殊的委托。 应用程序域? 答:应用程序域可以理解为一种轻量级的进程,起到安全的作用,占用资源小。 Class.forName作用? 答:调用该访问返回一个以字符串指定类名的类对象。 JDO? 答:JDO是java对象持久化的新的规范,为java data object的简称,也是一个用于存取某种数据仓库中的对象的标准化API。 CORBA? 答:CORBA标准是公共对象请求代理结构,用途为:用不同的程序设计语言书写,在不同的进程中运行,为不同的操作系统开发。 xml解析技术? 答:常用的DOM、SAX等 DOM:处理大型文件时性能下降的非常厉害,适合对xml的随机访问 SAX:事件驱动型的xml解析方法,适合对xml的顺序访问 jsp常用动作? 答:jsp:include 引入一个文件 jsp:useBean 实例化JavaBean jsp:setProperty 设置JavaBean属性 jsp:getProperty 输出JavaBean属性 jsp:forward 转发 CTS、CLS、CLR分别作何解释? 答:CTS 通用类型系统、CLS 通用语言规范、CLR 公共语言运行时。 Struts1和Struts2原理和区别? 答:Struts1和Struts2是两个完全不同的框架,Struts1以ActionServlet作为核心控制器,由ActionServlet负责拦截用户的所有请求。Struts2以核心控制器FilterDispatcher为基础,包含了框架内部的控制流程和处理机制。 Hibernate工作原理,Hibernate数据持久化? 答:Hibernate工作原理: 1:读取并解析映射信息,创建SessionFactory 2:打开Session 3:创建事物 4:持久化操作 5:提交事务 6:关闭Session 7:关闭SessionFactory Hibernate持久化:Hibernate根据定义的映射规则将对象持久化保存到数据库,这就实现了对象的持久化。 Spring由那几个模块组成? 答:Spring主要由7个模块组成: 1:Spring核心容器:提供了Spring框架的基本功能 2:Spring AOP:通过配置管理特性 3:Spring ORM:Spring框架集成了若干ORM框架 4:Spring DAO:打开关闭数据库连接 5:Spring WEB:为基于WEB服务的应用程序提供上下文服务 6:Spring Context:向Spring框架提供上下文信息 7:Spring MVC:分离模型、视图、控制器、以便更容易定制 折构函数和虚函数? 答:折构函数式销毁一个类的函数,虚函数是为了C++的动态绑定而设计的。 描述你的编程风格? 答:类名首字母大写,常量一般全部大写,给自己的代码加注释。 控制流程? 答:控制流程一般使用if判断条件。有第二分支,多分支,循环结构。循环本质上也是通过判断来实现的。 多形与继承? 答:多形:一个类中多个同名方法。继承:子类继承父类。 jsp内置对象? 答:request 用户端请求 response 回应 pageContext 网页属性 session 会话 out 输出 page 当前网页 exception 错误网页 application servlet正在执行的内容 config servlet构架部件 Struts模型驱动? 答:封装请求参数。 简述JDBC? 答:JDBC数据库连接,是一种用于执行SQL语句的Java API,可以为多种关系型数据库提供统一访问。 什么情况下不建议使用Hibernate? 答:当数据量大,并且表关系复杂的时候不建议使用。 sleep()和wait()有什么区别? 答:sleep()是线程类的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但监控状态依然保持,到时候会自动恢复。 wait()是Object类的方法,对此对象调用了wait方法导致本线程放弃对象锁,进入等待锁定池,只有针对此对象发出notify方法后本线程才进入对象锁定池准备获得对象锁进入运行状态。 同步和异步,在什么情况下分别使用? 答:如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。 当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步。在很多情况下采用异步往往更有效率。 数据库三大范式? 答:1:确保每列都是不可分割的原子值 2:确保每列都和主键相关 3:确保每列都和主键直接相关,而不是间接相关 单例模式有哪些好处? 答:单例模式是一种比较常见的设计模式,带给我们以下好处: 1:控制资源的使用 2:控制实例的产生数量 3:让多个不相关的两个线程或进程之间实现通信 为什么要用spring? 答:1、主要将各个框架有效的联系起来,缩短实际编程时间。 2、可以将各个框架进行有效的管理和控制,让数据传输安全。 spring的IOC和DI? 答:控制反转和依赖注入是spring的两大基础作用。主要是将所有组件在spring提供的外部容器中加载。提高安全性,减低耦合性,使各个框架或者类之间的依赖性降低。 什么是声明式的事务管理?为什么要用?spring如何实现声明式的事务管理? 答:声明式的事务管理主要是将在进行对数据库中数据的添加或者修改时需要执行事务管理,主要是为了避免在执行添加或修改的时候添加或修改不完全正确,导致数据丢失。spring使用AOP面向切面的思想进行事务管理的。 spring和Hibernate继承后,定义事务管理特性的时候查询为什么要定义为read-only? 答:因为添加、删除和更新都涉及到了数据库的修改,而查询并未涉及到数据库修改,所以只需要定义只读,这样可以提高效率,进行更加方便的事务管理。 请你谈谈对Hibernate OR映射的理解? 答:将数据库中的每一张表都映射成一个实体。 配置了lazy="true"一定会懒加载吗? 答:不一定,如果在配置中你也使用了fetch属性的话此时lazy就会失效。 Hibernate数据库标识与主键之间的认识? 答:标识是为了方便和简介映射文件,主键是为了让数据不会重复。 为什么每次请求都要创建一个Action对象? 答:Struts2每次请求的时候都会创建一个action实例,这样会保证线程的安全。Struts1只是在第一次请求的时候创建一个action实例,以后每次相同的请求都直接从内存中去读取,它是单例模式,安全性较差。 Struts2是如何实现MVC模式的? 答:在Struts2里面是将每次页面的请求进行处理,然后将请求需要输出的数据转发到需要做数据显示的页面。Struts2只是起到一个数据接收和转接的功能,就是Controller控制器,而传来数据的页面叫view显示层,Struts2将数据提交给进行处理的类叫Model模型层,专门进行数据处理和数据库的连接。 heap和stack有什么区别? 答:栈是一种线形集合,其添加和删除元素的操作应在同一段完成,栈按照后进先出的方式进行处理。堆是栈的一个组成元素。 EJB和JavaBean的区别? 答:EJB不是一般的JavaBean,EJB是企业级的JavaBean,EJB一共分为3种,实体Bean,消息Bean,会话Bean。书写EJB要遵循一定的规范,另外要运行EJB,你需要有相应的EJB容器,比如WebLogic、jboss等,而JavaBean不需要,只需要安装Tomcat就可以了。EJB用于服务端的应用开发,而JavaBean用于客户端应用开发。 触发器? 答:触发器是一种特殊的存储过程,主要通过事件来触发而被执行。 什么是存储过程?用什么调用? 答:存储过程是一个预编译的SQL语句,优点是允许模块化的设计。就是说只需要创建一次,以后再程序中就可以调用多次。使用存储过程比单纯的SQL语句要快,可以用一个命令对象来调用存储过程。 索引优缺点? 答:索引可以提高对数据库中数据的检索,缺点是减慢了数据录入速度,同时也增加了数据库的尺寸大小。 什么是事务?什么是事锁? 答:事务就是被绑定在一起,作为一个逻辑单元执行的SQL语句。如果任何一个操作失败,那么整个就失败。共同失败或共同成功。锁可以保证事务的完整性和并发性。 什么是视图?游标是什么? 答:视图是一种虚拟的表,具有和物理表相同的功能。游标是对查询出来的结果集作为一个单元来有效的处理,可以对结果集的当前行做修改。 select执行顺序? 答:from where group by having select order by Collection和Collections的区别? 答:Collection是集合类的父类,继承它的主要由set和list Collections是针对集合类的帮助类,它提供了一系列针对集合的搜索,排序,线程安全化等操作。 final、finally、finalize的区别? 答:final用于声明属性方法和类,分别表示:属性不可变,方法不可覆盖,类不可继承。 finally是异常处理语句的一部分,表示总是执行。 finalize是Object的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法。 assert是什么?什么时候用到? 答:断言,可以将断言看成是异常处理的一种高级形式,可以用来判断某个特定的表达式或值是否为真。 接口是否可以继承接口?抽象类是否可以实现接口?抽象类是否可以继承实体类? 答:接口可以继承接口,抽象类可以实现接口,抽象类可以继承实体类。 引用传递和值传递? 答:引用传递:形参改变影响实参 值传递:形参改变不影响实参 当一个线程进入一个对象的synchronized方法后,其他线程是否可进入此对象的其他方法? 答:其他方法前是否加了synchronized关键字,如果没加则能。 说出servlet生命周期? 答:servlet实例化时调用init方法,得到请求时调用service方法,service方法自动派遣doget或dopost方法,最后当实例销毁时调用destroy方法。 error和exception有什么区别? 答:error是指错误,通常程序员不能通过代码来解决。底层环境或硬件问题。 exception是指异常,可以通过代码来解决的问题。 forward和redirect的区别? 答:forward是转发,浏览器跳转后不显示新的地址。 redirect是重定向,浏览器跳转后显示新的地址。 对比之下forward更加高效,并且它有助于隐藏实际地址,但是有些情况则必须使用redirect,否则会报异常。 jsp中动态include和静态include的区别? 答:动态include用jsp:include实现,适用于动态页面,可以携带参数 静态include用include伪码实现,适用于静态页面 math.round(11.5)等于多少?math.round(-11.5)等于多少? 答:Math.round()对数字进行四舍五入 Math.round(11.5)=12 Math.round(-11.5)=11 String s=new String("xyz");创建了几个String Object? 答:创建了两个String对象,一个保存的引用地址,一个保存实际的值。 数组有没有length()这个方法?String呢? 答:数组里面没有length()方法,而是length属性。String有length()这个方法。 swtich()能否作用在byte、long、String上? 答:swtich()传递的应该是一个整数表达式,所以它的值只能是:int、short、char、byte所以long和String都不能作用在swtich()上。 jsp和servlet之间的联系? 答:jsp前段动态页面,servlet是纯java类 jsp被编译之后会转换为servlet执行 java基本数据类型有哪些?String是不是基本数据类型,他们有何区别? 答:基本数据类型8种:int、short、byte、long、float、double、char、boolean String不是基本数据类型,引用数据类型。 区别:基本数据类型比较实用“==”,引用数据类型实用equest,并且引用数据类型存放的是地址而不是具体的值。 写一个方法,实现字符串的替换,如:输入bbbwlirbbb,输出bbbhhtccc? 答:String s="bbbwlirbbb"; s.replaceAll("wlirbbb","hhtccc"); 如何将数值型字符转换为数字(Integer,Double)? 答:可以用Integer.parseInt()和Double.parseDouble()方法 如何将数字转换为字符? 答:可以使用String的valueOf()方法。 如何取得1970到现在的毫秒数? 答:可以用getTime()方法。 如何格式化日期? 答:可以用SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd"); 判断是否有子文件?判断是否有子目录?判断文件是否存在? 答:判断是否有子文件使用file.isFile() 判断是否有子目录使用file.isDirectory() 判断文件是否存在使用file.exists() 继承、重载、覆盖问题? 答:继承:子类继承父类,子类可以拥有父类中已定义的方法,并且子类可以修改父类中的方法使其更适合特殊需求。 重载:在一个类中方法名和返回类型相同,参数不同。 覆盖:在子类中覆盖父类的某个方法,要求方法名相同,参数类型相同。 Statement和PreparedStatement之间的区别? 答:Statement比PreparedStatement速度慢 PreparedStatement是预编译,插入时速度高于Statement Statement创建速度很慢,常用选择PreparedStatement Session机制? 答:session机制是一种服务器端机制,服务器使用一种类似于散列表的结构来保存信息。 jsp和servlet中的请求转发分别如何实现? 答:jsp可以使用jsp:forward标签转发 servlet可以使用request.getRequestDispatcher()实现转发 介绍j2ee、j2se、j2me的区别? 答:j2ee(企业版):主要用于企业web开发 j2se(标准版):主要用于web开发,但缺少企业版的一些特性 j2me(微小版):主要用于手机开发 J2ee提供的技术? 答:j2ee提供的技术有EJB、servlet、jsp等。 什么是Application Server? 答:Application Server 应用服务器 简单介绍连接池的优点和原理? 答:数据库连接和关闭是比较花费时间的一件事,而连接池可以提高我们的工作效率。 刚开始创建一定数量的池连接,当需要时从池连接中拿出一个,用完之后再把这个连接重新放回连接池。 Web.xml的作用? 答:Web.xml会在程序启动时执行,如果想在程序刚启动的时候执行一些方法的话可以配置在Web.xml中。 简单介绍您所了解的MVC? 答:在项目中使用MVC模式会使项目分工更加明确,结构更加清晰 M model 模型层:JavaBean V view 视图层:jsp html C controller 控制层:servlet 简单介绍你所了解的XML? 答:XML可扩展标记语言,可以用来标记数据,定义数据结构,是一种允许用户自定义标记的语言 简单介绍您所了解的structs、spring和hibernate? 答:struts:控制层Action,页面标签和Model数据,调用业务层 Spring:Aop管理事务控制,IOC管理各个组件的耦合 Hibernate:负责数据库和对象的映射,负责Dao层 因为你去公司面试,公司让你写笔试,不可能说XX一个题目写两页纸写的太详细,所以以上答案有的可能比较简短,没有说出重点,请大家补充,如果大家有什么更好的答案请拿出来一起分享
JAVA相关基础知识 1、面向对象的特征有哪些方面 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。抽象包括两个方面,一是过程抽象,二是数据抽象。 2.继承: 继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。 3.封装: 封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。 4. 多态性: 多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。 2、String是最基本的数据类型吗? 基本数据类型包括byte、int、char、long、float、double、boolean和short。 java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类 3、int 和 Integer 有什么区别 Java 提供两种不同的类型:引用类型和原始类型(或内置类型)。Int是java的原始数据类型,Integer是java为int提供的封装类。Java为每个原始类型提供了封装类。 原始类型封装类 booleanBoolean charCharacter byteByte shortShort intInteger longLong floatFloat doubleDouble 引用类型和原始类型的行为完全不同,并且它们具有不同的语义。引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题,这种类型以哪种类型的数据结构存储,当引用类型和原始类型用作某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为 null,而原始类型实例变量的缺省值与它们的类型有关。 4、String 和StringBuffer的区别 JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要改变的时候你就可以使用StringBuffer。典型地,你可以使用StringBuffers来动态构造字符数据。 5、运行时异常与一般异常有何异同? 异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。 6、说出Servlet的生命周期,并说出Servlet和CGI的区别。 Servlet被服务器实例化后,容器运行其init方法,请求到达时运行其service方法,service方法自动派遣运行与请求对应的doXXX方法(doGet,doPost)等,当服务器决定将实例销毁的时候调用其destroy方法。 与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。 7、说出ArrayList,Vector, LinkedList的存储性能和特性 ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。 8、EJB是基于哪些技术实现的?并说出SessionBean和EntityBean的区别,StatefulBean和StatelessBean的区别。 EJB包括Session Bean、Entity Bean、Message Driven Bea

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 132
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

栗筝i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值