开发小型门户网站的方法 步骤,网站备案登记表,做最精彩绳艺网站,湖南网站设计外包费用基础篇 面向对象的特征 封装#xff08;Encapsulation#xff09;#xff1a; 封装是指将对象的数据#xff08;属性#xff09;和行为#xff08;方法#xff09;结合在一起#xff0c;形成一个独立的实体。对象的数据被隐藏在内部#xff0c;只能通过定义好的接口Encapsulation 封装是指将对象的数据属性和行为方法结合在一起形成一个独立的实体。对象的数据被隐藏在内部只能通过定义好的接口方法来访问。这样可以防止外界随意改变对象内部的状态保证了数据的安全性和完整性。 继承Inheritance 继承允许新创建的类子类或派生类继承现有类父类或基类的属性和方法。子类可以重用父类的代码可以扩展或定制父类的功能。继承提供了代码重用的能力并且可以建立起类之间的层次关系。 多态Polymorphism 多态性意味着可以使用相同的接口调用不同的基于对象类型的方法。在编程中多态通常体现为“一个接口多个实现”它允许将对象视为其自身的类型或其父类型的实例并根据对象的实际类型来调用相应的方法。多态性增加了代码的灵活性和扩展性。 抽象Abstraction 抽象是将复杂的现实问题简化为模型的过程。在面向对象编程中抽象意味着隐藏复杂的实现细节只展示对象的关键特性。一个抽象类或接口可以定义方法和属性的“蓝图”具体的实现则由子类负责。 2 在Java编程语言中final、finally和finalize是三个有明显不同用途的概念。 final final是一个访问修饰符。它可以修饰类、方法和变量。 当final修饰一个类时表明这个类不能被继承。当final修饰一个方法时表示这个方法不能被子类覆盖。当final修饰一个变量包括成员变量和局部变量时表示这个变量的值一旦被初始化之后就不能再被改变对于引用类型其引用不能再指向另一个对象但对象的内部状态是可以改变的。 finally finally是与try和catch语句一起使用的在异常处理中起着重要的作用。无论是否抛出或捕获异常finally块中的代码都会被执行。因此finally通常用于进行清理工作如关闭文件、释放资源等。 1try { 2 // 可能会抛出异常的代码 3} catch (ExceptionType name) { 4 // 处理异常 5} finally { 6 // 清理代码无论是否抛出异常都会执行 7} finalize finalize是一个方法它定义在java.lang.Object类中。它在垃圾收集器确定不存在对该对象的更多引用时被垃圾收集器调用用于执行对象销毁前的清理工作。finalize方法可以被子类覆盖以实现特定的清理逻辑。 1Override 2protected void finalize() throws Throwable { 3 try { 4 // 清理资源如释放网络连接或关闭文件 5 } finally { 6 super.finalize(); // 调用超类的finalize方法 7 } 8}
需要注意的是finalize方法并不推荐使用因为它的执行时间是不确定的且容易导致错误和性能问题。从Java 9开始finalize方法已经被声明为过时deprecated因此应该避免使用并寻找其他资源管理的方式比如使用try-with-resources语句自动管理资源
3 int 和 Integer 有什么区别
int 和 Integer 在Java中是两种不同的类型它们之间的主要区别在于int是原始数据类型primitive data type而Integer是包装类wrapper class。
以下是int和Integer之间的一些关键区别 int int是Java的一种原始数据类型用于表示32位有符号的整数值。int类型的变量直接存储数值本身因此它的处理效率较高。作为原始类型int变量不能调用方法因为它不是对象。默认值为0。 Integer Integer是int的包装类属于Java的一部分基本类型的包装类位于java.lang包中。Integer是一个类因此Integer的变量实际上是一个对象的引用这个对象包含了一个int类型的值。由于Integer是一个类它可以有方法例如可以调用.toString()来将整数转换成字符串或者调用.equals()来比较两个Integer对象的值。Integer对象可以为null表示它还没有指向任何实际的整数值这是区分于int的0值。Integer类还提供了许多静态方法例如用于解析字符串的.parseInt()或者用于比较两个整数的.compare()。
从Java 5开始引入了自动装箱autoboxing和自动拆箱unboxing特性 自动装箱当需要Integer对象时原始int类型的值会自动转换为Integer对象。 1int i 10; 2Integer integer i; // 自动装箱 自动拆箱当需要原始int类型的值时Integer对象会自动转换为int值。 1Integer integer new Integer(10); 2int i integer; // 自动拆箱
由于自动装箱和拆箱的存在int和Integer之间的转换在大多数情况下对开发人员是透明的但是仍然需要注意空指针异常NullPointerException因为Integer对象可以为null而int类型不能。
在实际编程过程中选择int还是Integer取决于具体情况。如果要利用对象的特性或者需要使用集合类如ArrayList则必须使用Integer但如果追求性能或者处理基本数值运算通常会使用int。
4.重载和重写的区别
重载Overloading和重写Overriding是面向对象编程中两种不同的概念它们都允许程序员在一定程度上修改类的行为。但是它们的用途和规则有所不同。 重载Overloading 概念重载是指在同一个类中定义多个名称相同但参数列表不同的方法。方法的返回类型可以相同也可以不同并且重载与方法的访问修饰符或抛出的异常也没有关系。目的重载使得程序员可以使用相同的方法名执行不同的功能提高了方法的可用性。规则要实现重载至少要改变方法的参数数量或参数类型。只改变返回类型不足以构成重载。范围只能在同一个类中进行。 1public class Example { 2 public void display(String s) { 3 // ... 4 } 5 public void display(int i) { // 重载方法 6 // ... 7 } 8} 重写Overriding 概念重写是指子类重新定义父类中已有的方法。子类的方法必须与父类被重写的方法具有相同的方法名称、参数列表和返回类型。目的重写允许子类提供特定于自己的实现这是实现多态性的基础。规则 方法名、参数列表必须完全相同。返回类型必须相同或是协变返回类型子类重写方法的返回类型可以是父类方法返回类型的子类型。访问修饰符可以保持相同或者更宽松但不能更严格。重写方法不能抛出新的检查异常或者比被重写方法更广泛的检查异常。使用Override注解可以明确指示一个方法是重写方法有助于编译器检查和提高代码可读性。 1public class Base { 2 public void display() { 3 // ... 4 } 5} 6 7public class Derived extends Base { 8 Override 9 public void display() { // 重写方法 10 // ... 11 } 12}
综上所述方法的重载发生在同一个类中或者在子类中它只关心方法签名方法名和参数列表而不考虑方法的返回类型方法的重写专指子类覆盖父类的同一个方法需要保证方法签名和返回类型与被重写的父类方法一致是实现运行时多态的关键。 Java接口默认方法 自Java 8起接口支持定义默认方法。默认方法的定义格式为public default 返回值类型 方法名称(参数列表)。这里的public可以略去不写但default是必需的。默认方法可以在接口中定义并且不需要实现类为其提供实现。这意味着当一个新的接口特性被添加时所有已存在的实现类都可以自动获得这个新的功能而无需修改它们各自的实现代码。 默认方法的使用有两种情况 可以直接在实现类中使用接口中的默认方法就像使用普通接口方法一样。同样地实现类可以对接口中的默认方法进行覆盖重写以根据自己的需求定制逻辑。 例如以下是一个带有默认方法和实现类的简单接口示例 java public interface MyInterface { default void defaultMethod() }
public class DefaultTest implements MyInterface {
Override
public String getBrand() {
return iPhone;
}
} 从Java 9开始接口还可以定义静态方法和私有方法。静态方法不能通过接口实现类进行调用而是需要通过接口名称直接调用。私有方法则是为了在不暴露具体实现细节的情况下提供一个共用的逻辑块。 接口中的常量可以被定义为public static final这样的成员变量将被认为是不可变的。接口中的常量可以是数据值也可以是构造器但这取决于具体的编程风格和使用场景。
5.抽象类和接口有什么区别
抽象类Abstract Class和接口Interface都是Java中支持抽象概念的结构它们都不能被实例化可以包含抽象方法即没有具体实现的方法。但是它们之间存在一些关键的区别 设计目的 抽象类通常作为多个类的共同的父类使用用来封装这些类中的共同特征包括字段和方法。接口则主要用来规定一个形式标准Contract是一种形式化的声明它定义了类必须实现的方法但不关注这些方法的实现细节。 方法实现 抽象类可以包含抽象方法和具体方法即可以有方法的实现也可以没有。接口只能包含抽象方法在Java 8之前的版本在Java 8及之后的版本中接口可以包含默认方法default methods和静态方法static methods默认方法可以有具体实现。 状态存储 抽象类可以包含成员变量fields这些变量可以是非常量的因此可以在抽象类中定义状态state。接口只能包含常量在Java 8之前即默认为public static final的变量。在Java 8及之后的版本中接口不能包含实例字段但可以包含静态字段。 构造函数 抽象类可以包含构造函数而接口不能。 继承和实现 一个类只能继承自一个抽象类这是因为Java不支持多重类继承。一个类可以实现多个接口提供了一种形式的多重继承。 访问修饰符 抽象类中的成员可以有多种访问修饰符可以是public、protected或private。接口中的方法和变量默认都是public的在Java 9及之后的版本中接口还可以包含私有方法private methods这些方法是不会被继承的。 扩展性 向抽象类添加新方法可能会破坏子类。接口是一种更加灵活的结构尤其是在Java 8之后可以通过默认方法添加新行为而不破坏实现该接口的类。
根据你的具体需求来选择使用抽象类还是接口。如果你的抽象概念更侧重于是什么is-a那么使用抽象类如果它更侧重于能做什么can-do或具有什么功能has-a那么使用接口。随着Java语言的发展接口的功能得到了增强如默认方法这使得接口在某些情况下可以作为抽象类的替代品。
6.说说反射的用途及实现
反射Reflection是一种强大的特性允许Java程序在运行时检查或修改其自身的结构和行为。反射提供了一种机制通过它可以在运行时获取类的信息比如类的方法、字段、构造器等并动态地创建对象、调用方法、访问字段和构造器等。这种动态性使得Java程序更加灵活和强大。
反射的主要用途包括 运行时类信息获取可以在运行时获取类的信息例如类的名字、修饰符、包信息、父类、实现的接口、方法、字段等。 动态创建对象可以在不知道类名的情况下创建对象实例这对于扩展性和灵活性有很大帮助。 动态调用方法可以在运行时调用任何方法无需提前编码这些调用这能够大大增加程序的灵活性。 动态访问和修改字段可以在运行时访问和修改对象的字段无论它们原本的访问权限如何。 实现通用代码反射允许编写更通用的代码这些代码可以与许多不同类型的对象一起工作。 框架和库许多流行的框架如Spring和库使用反射来实现依赖注入、ORM对象关系映射、远程方法调用等功能。
反射的实现通常涉及以下几个步骤 获取Class对象的引用可以通过直接使用.class语法、调用对象的.getClass()方法或者使用Class.forName()静态方法实现。 1Class? cls1 String.class; 2Class? cls2 hello.getClass(); 3Class? cls3 Class.forName(java.lang.String); 分析类的能力使用Class对象的方法来检索类的信息。 1// 获取类的方法 2Method[] methods cls1.getDeclaredMethods(); 3 4// 获取类的字段 5Field[] fields cls1.getDeclaredFields(); 6 7// 获取类的构造器 8Constructor?[] constructors cls1.getDeclaredConstructors(); 创建实例使用Class对象的newInstance()方法或者获取到的构造器对象来创建类的实例。 1Object obj1 cls1.newInstance(); // Deprecated since Java 9 2Constructor? constructor cls1.getConstructor(String.class); 3Object obj2 constructor.newInstance(constructor-arg); 访问字段和调用方法使用Field和Method对象来访问字段和调用方法。 1// 访问字段 2Field field cls1.getField(someField); 3Object fieldValue field.get(obj); 4 5// 调用方法 6Method method cls1.getMethod(someMethod, String.class); 7method.invoke(obj, method-arg); 修改字段的值可以使用Field对象的set()方法来修改字段的值即使它是私有的。 1field.setAccessible(true); // 设置为可访问忽略访问权限 2field.set(obj, new value);
反射虽然功能强大但也存在一些缺点
性能开销反射操作通常比直接代码调用要慢因为它需要在运行时分析类的元数据。安全限制反射可能会打破封装性改变原本私有的字段和方法这可能会导致安全问题或者代码的不稳定。维护难度由于反射代码的动态性它可能难以理解和维护。
使用反射时应该权衡利弊并在确实需要动态性时才使用。在某些情况下可以考虑使用其他编程技术或模式如设计模式来达到类似的结果同时保持更好的性能和代码可维护性
7.说说自定义注解的场景及实现
自定义注解Custom Annotations在Java中是一种特殊的语法元素它允许为代码添加元数据这些元数据可以在编译时、类加载时或者运行时被读取并且可以影响程序的行为。自定义注解广泛用于提供配置信息、编译检查、代码分析、测试框架等场景。
自定义注解的常见场景 配置框架 框架如Spring使用注解来配置应用程序组件和依赖注入例如Component、Service、Autowired等。 代码校验 注解可用于静态代码分析工具如检查是否遵守某些编码标准或寻找潜在的错误。 编译时处理 创建编译时注解处理器可以在编译时生成额外的源代码或资源文件。 测试代码 测试框架如JUnit使用注解来标识测试方法Test、设置测试环境Before、After等。 Web应用开发 Web框架使用注解来定义路由、请求和响应类型等如JAX-RS的Path、GET、POST等。 持久层框架 ORM框架如Hibernate和JPA使用注解来映射对象到数据库表。
实现自定义注解
要实现自定义注解需要使用interface关键字并且可以选择性地为注解定义成员。成员在使用注解时以键值对的形式提供如果有默认值可以省略。 1import java.lang.annotation.*; 2 3// 定义注解的保留策略和目标 4Retention(RetentionPolicy.RUNTIME) 5Target(ElementType.METHOD) 6public interface MyAnnotation { 7 // 定义注解的成员 8 String value() default Default Value; // 带默认值的成员 9 int number() default 0; // 另一个带默认值的成员 10}
注解成员的类型限制为原始类型、String、Class、枚举、注解以及前述类型的数组。
使用自定义注解
自定义注解可以被应用到类、方法、字段等元素上取决于注解的Target定义。 1public class Example { 2 MyAnnotation(value Custom Value, number 42) 3 public void myMethod() { 4 // 方法实现 5 } 6}
读取自定义注解
在运行时可以通过反射API读取注解并执行相应的处理。 1Method method Example.class.getMethod(myMethod); 2if (method.isAnnotationPresent(MyAnnotation.class)) { 3 MyAnnotation myAnnotation method.getAnnotation(MyAnnotation.class); 4 System.out.println(value: myAnnotation.value()); 5 System.out.println(number: myAnnotation.number()); 6}
8HTTP 请求的 GET 与 POST 方式的区别 HTTP超文本传输协议定义了客户端与服务器之间交换数据的不同方法其中最常用的是GET和POST请求。这两种请求方法有一些关键的区别 用途和语义 GET请求通常用于请求服务器发送资源。按照HTTP规范的语义GET请求应该是幂等的也就是说它不应该引起服务器状态的变化。POST请求通常用于提交数据给服务器如表单提交。与GET不同POST请求可能会引起服务器状态的变化或副作用比如在数据库中创建一条记录。 参数传递 GET请求的参数通常附加在URL之后以查询字符串的形式出现例如?key1value1key2value2。这意味着GET请求的参数会被完全暴露在地址栏中。POST请求的参数则包含在请求体中不会显示在URL中因此可以发送更多的数据并且提供了更好的隐私。 数据大小 GET请求因为受到URL长度的限制不同浏览器和服务器对URL长度的限制不同因此无法发送大量数据。POST请求没有URL长度限制的问题理论上可以发送更大量的数据因此适合表单提交、文件上传等场景。 安全性 GET请求由于参数在URL中所以安全性较低敏感信息不应该通过GET请求传送。POST请求更加安全因为数据不会保存在浏览器历史或服务器日志中。 缓存和历史 GET请求可以被缓存并且会留在浏览器的历史记录中。POST请求不会被缓存通常也不会留在浏览器的历史记录中。 书签和分享 GET请求可以被书签也更容易分享复制粘贴URL。POST请求不可以直接书签因为它包含了请求体无法通过URL完整表示。 幂等性 GET请求应该是幂等的意味着多次执行相同的GET请求服务器上的资源状态不会改变。POST请求一般不是幂等的同样的POST请求如果多次执行可能会每次都产生副作用如创建多个资源。 使用场合 使用GET请求时主要用于获取信息例如搜索、查询、阅读页面等操作。使用POST请求时主要用于修改服务器上的资源例如用户注册、提交订单等操作。
总结来说GET和POST请求在HTTP协议中有着不同的语义和使用场景。选择正确的方法将有助于遵循HTTP协议的设计意图并确保Web应用的可靠性和效率
9.session 与 cookie 区别
Session和Cookie都是用于存储客户端和服务器交互中需要持久化的信息的机制但它们在处理方式、存储位置和安全性等方面存在一些差异。
Cookie 存储位置Cookie数据存储在客户端通常是浏览器。 存储内容Cookie主要用于存储字符串类型的小数据量信息。 安全性由于存储在客户端Cookie数据容易被篡改和拦截相对不那么安全。为了提高安全性可以使用加密Cookie和设置HttpOnly标志。 生命周期Cookie可以设置过期时间。如果不设置过期时间它就是一个会话Cookie浏览器关闭时就会被删除。设置了过期时间后Cookie可以在本地持久化直到过期时间。 发送请求时每次客户端请求服务器时Cookie都会自动添加到请求头中发送给服务器。 大小限制Cookie有大小限制不同浏览器有不同的限制一般为4KB左右而且每个域名下存储的Cookie数量也有限制。
Session 存储位置Session数据存储在服务器端。 存储内容Session可以存储任何类型的数据包括对象和大量数据。 安全性相较于CookieSession更安全因为数据存储在服务器端客户端无法直接访问。 生命周期Session的生命周期通常由服务器控制一般是基于用户的会话时间。用户关闭浏览器或服务器端超时设置都可能导致Session结束。 发送请求时为了维持会话客户端通常需要带上一个Session标识例如一个名为JSESSIONID的Cookie服务器通过这个标识来查找对应的Session数据。 大小限制Session没有大小限制但存储过多数据会增加服务器的内存压力。
Session和Cookie的比较 生命周期Cookie可以在客户端本地存储而Session存在于服务器上的会话时间内。 安全性Session比Cookie安全因为Session数据存储在服务器端。 存储能力Session可以存储更多信息而Cookie受到大小和数量的限制。 服务器资源Session数据存储在服务器上如果大量使用将占用较多服务器资源。Cookie则存储在客户端不占用服务器资源。 跨域问题Cookie设置时可以设置其对应的域domain和路径path实现跨域访问的控制而Session通常只在同一域中有效不同域的Session管理需要特殊的处理。
在实际应用中为了结合两者的优势经常会将Session ID存储在Cookie中以此标识用户的会话。这样既利用了Session的安全性又使用Cookie方便的客户端存储机制来维持会话状态
9 session 分布式处理
在分布式系统中由于应用可能部署在多个服务器上单机版的session管理已经不能满足需求我们需要确保用户的会话信息在所有服务器之间同步以便用户可以无缝地与任何服务器交互。以下是几种常见的分布式session处理方案 粘性SessionSticky Sessions 配置负载均衡器使得来自同一个用户的所有请求都转发到同一台服务器。这是最简单的方法但如果那台服务器崩溃用户的会话信息将丢失。 Session复制 在服务器集群中复制session信息。每次session更新时这些更新都会发送到集群中的其他服务器。这种方法的缺点是它会占用额外的网络带宽并且随着集群规模的扩大复制的成本增加。 集中式Session存储 将所有session信息存储在集中的数据存储中例如数据库或内存缓存如Redis、Memcached。任何服务器都可以从该存储中读取和写入session信息。这种方法的优点是可以扩展性好而且对服务器故障有更好的容错性。 客户端Session 将session数据存储在客户端通常是以Cookie的形式。每次HTTP请求都会发送session数据因此这种方案可能会导致数据暴露或被篡改的风险。可以通过加密和签名来增强安全性但这会增加处理请求的复杂性。 Token-based身份验证如JWT 使用基于令牌的方法例如JSON Web Tokens, JWT来管理会话。服务器验证用户的身份并发放一个签名的token客户端随后的每个请求都携带这个token。服务器通过验证token的签名来认证用户而不需要存储会话信息。这种方法减少了服务器的存储需求但需要额外的考虑来保证token的安全性。
实现分布式Session存储的考虑因素
一致性需要确保所有服务器看到的session信息都是一致的。可用性集中式session存储应该具有高可用性以避免成为单点故障。安全性存储session数据时需要确保数据安全避免泄露用户敏感信息。性能读取和写入session数据应该尽可能快以避免降低用户体验。扩展性随着用户数量的增长session管理系统应该能够容易扩展。
总之分布式session管理是构建可扩展和高可用性应用程序的关键。在实施任何分布式session解决方案时都需要权衡不同方案的利弊并考虑应用的具体需求和限制。 10 JDBC 流程
JDBCJava Database Connectivity是Java语言中用于与数据库进行交互的API它定义了一组接口和类使得Java应用程序能够统一地访问各种关系型数据库。使用JDBC进行数据库操作通常涉及以下步骤 加载JDBC驱动 在应用程序中加载并注册数据库的JDBC驱动这样程序就能知道如何与特定的数据库通信。Java 6及以后的版本会自动从类路径中加载驱动所以通常不需要显式加载驱动。 1// 在Java 6之前的版本中常用的显式加载驱动的代码 2Class.forName(com.mysql.cj.jdbc.Driver); 建立数据库连接 通过DriverManager类获取数据库的连接Connection。需要提供数据库的URL、用户名和密码。 1String url jdbc:mysql://localhost:3306/databaseName; 2String username username; 3String password password; 4Connection conn DriverManager.getConnection(url, username, password); 创建Statement对象 通过Connection对象创建Statement、PreparedStatement或CallableStatement对象。Statement用于执行静态SQL语句PreparedStatement用于执行预编译的SQL语句而CallableStatement用于执行存储过程。 1Statement stmt conn.createStatement(); 2PreparedStatement pstmt conn.prepareStatement(SELECT * FROM table WHERE column ?); 3CallableStatement cstmt conn.prepareCall({call stored_procedure(?, ?)}); 执行SQL语句 使用Statement对象执行SQL语句。根据操作类型的不同可能会调用executeQuery用于SELECT查询、executeUpdate用于INSERT、UPDATE、DELETE语句或execute对于更通用的情况方法。 1ResultSet rs stmt.executeQuery(SELECT * FROM table); 2int rowsAffected pstmt.executeUpdate(); 3boolean hasResults cstmt.execute(); 处理结果集 对于查询操作执行SQL语句会返回一个ResultSet对象程序需要遍历这个对象来处理查询结果。 1while (rs.next()) { 2 String data rs.getString(column_name); 3 // 处理获取到的数据 4} 关闭资源 使用完数据库资源后应该关闭ResultSet、Statement和Connection对象释放占用的资源。最好在finally块中或使用try-with-resources语句来确保这些资源总是被正确关闭。 1// 使用try-with-resources语句自动关闭资源 2try (Connection conn DriverManager.getConnection(url, username, password); 3 Statement stmt conn.createStatement(); 4 ResultSet rs stmt.executeQuery(SELECT * FROM table)) { 5 while (rs.next()) { 6 // 处理结果集 7 } 8} catch (SQLException e) { 9 e.printStackTrace(); 10}
以上就是一个典型的JDBC操作数据库的流程。在实际应用中JDBC代码通常会更加复杂需要处理异常、事务控制、连接池管理等。因此很多开发者会选择使用ORM框架如Hibernate、MyBatis来简化数据库操作这些框架内部依然使用JDBC技术与数据库通信。
11.MVC 设计思想
MVCModel-View-Controller设计思想是一种软件架构模式它将应用程序分为三个相互协作的部件或层以实现业务逻辑、数据处理和用户界面展示之间的解耦。每个部分负责不同的职责
模型Model
模型层代表了应用程序的核心业务逻辑和数据结构。 它包含了所有与应用状态和数据相关的操作如数据库访问、业务规则验证、数据计算等。 当模型的状态发生变化时通常会通知相关视图进行更新。 视图View
视图层是用户看到并与之交互的界面部分。 视图从模型获取数据并将其呈现给用户但它不直接操作数据而是显示由模型提供的数据。 视图通常依赖于模型的数据变化通过监听或数据绑定机制来自动刷新界面。 控制器Controller
控制器层负责接收用户的输入请求并根据请求执行相应的业务逻辑。 控制器解析用户的操作调用模型的方法来处理数据并决定应该向哪个视图传递这些结果或触发什么样的视图更新。 在Web开发中控制器类通常使用注解如Controller或RestController来定义路由处理函数它们作为HTTP请求和响应处理的入口点。 采用MVC设计模式的好处包括
分离关注点各层专注于自己的功能便于代码维护和扩展。 可重用性视图可以独立于模型和控制器改变反之亦然。 灵活性可以根据需求更改视图的表现形式而无需修改模型或底层业务逻辑。 测试方便各个组件可以独立测试降低了模块间相互影响导致的测试复杂度。 在现代Web框架如Spring MVC、ASP.NET MVC等中MVC模式得到了广泛应用和发展虽然具体的实现细节可能有所不同但核心理念仍然保持一致。
12.equals 与 的区别
在 Java 中equals 方法和 操作符都用于比较两个对象但它们的比较方式和使用场景有所不同。 操作符
基本类型当用于基本数据类型如 int、char、double 等时 比较的是值是否相等。引用类型当用于引用类型如对象时 比较的是两个引用是否指向内存中的同一个对象实例。
例如 1String str1 new String(example); 2String str2 new String(example); 3boolean result (str1 str2); // 结果为 false因为 str1 和 str2 指向不同的对象实例
equals 方法
equals 是 Object 类的一个非静态方法它用于检查两个对象的内容是否相等。默认实现即 Object 类中的实现是使用 操作符来比较两个对象的引用。通常当创建一个类时如果需要基于对象的内容而不是对象引用来判断相等性就应当重写 equals 方法。例如String 类重写了 equals 方法使得它可以比较两个字符串的内容是否相等。 1String str1 new String(example); 2String str2 new String(example); 3boolean result str1.equals(str2); // 结果为 true因为 str1 和 str2 的内容相同
比较 用于基本类型的值比较和引用类型的引用比较。equals 用于比较两个对象的内容是否相等但需要注意如果没有在类中重写 equals 方法该方法的行为默认是比较对象的引用与 相同。
重要点
当重写 equals 方法时应该始终遵守以下约定以确保该方法的行为符合开发者的期望
自反性对于任何非空引用值 xx.equals(x) 应该返回 true。对称性对于任何非空引用值 x 和 yx.equals(y) 应该返回 true 当且仅当 y.equals(x) 返回 true。传递性对于任何非空引用值 x、y 和 z如果 x.equals(y) 返回 true 并且 y.equals(z) 返回 true那么 x.equals(z) 也应该返回 true。一致性对于任何非空引用值 x 和 y多次调用 x.equals(y) 应该一致地返回 true 或一致地返回 false前提是对象上的信息没有被修改。对 null 的处理对于任何非空引用值 xx.equals(null) 应该返回 false。
在重写 equals 方法时通常也需要相应地重写 hashCode 方法以保持 equals 和 hashCode 之间的一致性合同这对于将对象作为散列集如 HashSet中的键来说是必要的。 13. string、StringBuilder、StringBuffer的区别
在 Java 中String、StringBuilder 和 StringBuffer 都用于处理字符串但它们在功能和性能方面有所不同。
String String 类表示不可变的字符序列。在 Java 中每次对字符串进行修改例如通过拼接、替换等操作实际上都会创建一个新的 String 对象而不是修改原有对象。由于字符串不可变它们在多线程环境下是线程安全的。 由于 String 对象不可变频繁的字符串操作可能会导致内存和性能开销。例如 1String s Hello; 2s World; // 这里实际上创建了一个新的 String 对象
StringBuilder StringBuilder 类表示可变的字符序列。它是 Java 5 引入的提供了一系列用于操作字符串内容的方法例如 append、insert、delete 等。由于 StringBuilder 是可变的它可以在不生成新对象的情况下修改字符串。 StringBuilder 不是线程安全的。在单线程环境中对性能要求较高的字符串操作通常应该使用 StringBuilder。例如 1StringBuilder sb new StringBuilder(Hello); 2sb.append( World); // 直接在原有字符串的基础上追加没有创建新对象
StringBuffer StringBuffer 类与 StringBuilder 类似也表示可变的字符序列提供了类似的字符串操作方法。不同之处在于 StringBuffer 是线程安全的所有公共方法都是同步的这意味着它可以在多线程环境中安全使用。 由于 StringBuffer 的线程安全特性它通常比 StringBuilder 慢。如果不需要线程安全通常建议使用 StringBuilder因为它的性能更优。例如 1StringBuffer sbf new StringBuffer(Hello); 2sbf.append( World); // 线程安全的追加操作
总结
String不可变字符串类简单使用时最为方便但不适用于大量频繁的字符串修改操作。StringBuilder可变字符串类适用于单线程下需要频繁修改字符串的场景性能优于 StringBuffer。StringBuffer可变字符串类线程安全适用于多线程下需要频繁修改字符串的场景。
在选择使用 String、StringBuilder 或 StringBuffer 时应当根据实际需求和上下文来做出决策。通常在循环、大量字符串操作或拼接时选择 StringBuilder 或 StringBuffer而在字符串相对固定且操作较少的场合使用 String。
14.List 和 Set 区别
Java中的List和Set都是集合框架的一部分它们都继承自Collection接口但具有不同的特性
列表List
特性有序、可重复 插入顺序与迭代顺序相同。 支持通过索引访问元素因此可以快速进行随机访问。 允许包含多个null元素但不是推荐的做法。 常用实现类包括ArrayList、LinkedList、Vector等。 ArrayList基于动态数组实现查询快增删慢LinkedList基于双向链表实现查询慢增删快。 集合Set
特性无序不保证插入顺序、不可重复 不支持索引访问仅能通过迭代器遍历元素。 只允许包含一个null元素在某些实现中如HashSet可能不允许任何null值。 Set中的元素唯一性由其equals()方法决定。 常用实现类包括HashSet、TreeSet、LinkedHashSet等。 HashSet基于哈希表实现检索效率高插入删除效率也较高并且不保证元素的插入顺序TreeSet基于红黑树实现自动排序元素根据自然顺序或自定义比较器插入删除操作比HashSet慢些但是提供了排序功能LinkedHashSet结合了HashSet和LinkedList的特点它保持元素的插入顺序并且不允许重复。 总结来说如果你需要维护元素的顺序和允许重复元素那么选择List如果你关心元素的唯一性而不关注它们的存储顺序或者有特定排序需求则应该使用Set。
15.List 和 Map 区别
Java中的List和Map是两种不同类型的集合它们分别用于存储不同类型的数据结构并且具有不同的功能和使用场景 列表List 类型有序、可重复的元素序列。特性 元素是有序的即插入顺序与迭代顺序相同除非进行了排序操作。支持索引访问可以通过下标快速获取或修改指定位置的元素。可以包含重复元素只要equals()方法判断为不相等即可。常用实现类包括ArrayList、LinkedList和Vector。 映射Map 类型键值对集合其中每个键与一个值相关联。特性 键不可重复根据键唯一性来确定元素的唯一性键的hashCode()和equals()方法共同决定键的唯一性。不支持索引访问而是通过键来查找对应的值。值可以重复即使多个键对应相同的值也是允许的。Map中元素没有明确的顺序尽管某些实现如LinkedHashMap会按照插入顺序或最近最少使用(LRU)顺序进行迭代。常用实现类包括HashMap、TreeMap、LinkedHashMap等。
总结来说List适用于需要维护元素顺序并且可能有重复数据的场景而Map则适用于需要通过唯一的键来关联和检索值的场景它不关心元素的顺序除非特殊实现并且确保键的唯一性。