wap网站一键生成app,网站备案账号是什么情况,ppt做的模板下载网站,手机触屏网站开发教程一、Java基础
1. final 关键字的作用#xff1a;
修饰类时#xff0c;被修饰的类无法被继承。修饰方法时#xff0c;被修饰的方法无法被重写。修饰变量时#xff0c;变量为常量#xff0c;初始化后无法重新赋值。
2. static 关键字的作用#xff1a;
修饰变量和方法时…一、Java基础
1. final 关键字的作用
修饰类时被修饰的类无法被继承。修饰方法时被修饰的方法无法被重写。修饰变量时变量为常量初始化后无法重新赋值。
2. static 关键字的作用
修饰变量和方法时被修饰的变量和方法是静态的可以直接通过类来引用而不需要创建实例。修饰代码块时是静态代码块在类加载时自动加载只执行一次。修饰内部类时是静态内部类只能访问外部类的静态成员变量和方法。静态内部类可以单独创建。修饰导入包中的静态方法或变量时可以直接使用被修饰的方法和变量而不需要加上所属的类。
3. 基本类型和引用类型的区别
在Java编程语言中数据类型可以分为两大类基本数据类型又称原始数据类型和引用数据类型。这两者的区别主要体现在以下几个方面
存储内容
基本数据类型直接存储实际值在栈Stack中例如数值、字符或布尔值。引用数据类型存储堆Heap内存地址在栈中该地址指向实际数据所在的位置。
内存分配
基本数据类型在变量声明时系统会在栈上为其分配空间并直接存储值。引用数据类型声明引用变量时栈上分配的空间存放的是对象的内存地址对象本身的数据存储在堆上。
数据类型种类
基本数据类型包括整数类型byte、short、int、long、浮点类型float、double、字符类型char和布尔类型boolean。引用数据类型包括类Class、接口Interface、数组Array、枚举Enum、注解Annotation和字符串String等。
使用方式
基本数据类型可以直接使用算术运算符进行操作比如加减乘除。引用数据类型不能直接使用算术运算符除了和比较地址但可以调用其方法和属性。
传递方式
基本数据类型作为方法参数传递时传递的是数据的值值传递。引用数据类型作为方法参数传递时传递的是内存地址引用传递因此方法内部对对象的修改会影响原始对象。
默认值
基本数据类型具有默认值例如整型的默认值是0布尔型的默认值是false。引用数据类型默认值是null。
性能
基本数据类型由于存储在栈上访问速度相对较快。引用数据类型存储在堆上需要通过栈上的引用访问速度相对较慢- 注意在Java中当谈到引用数据类型的参数传递时通常指的是“引用传递”pass by reference但这可能会引起一些误解。 在Java中所有参数传递都是按值传递pass by value包括引用数据类型。 这意味着当我们将一个引用数据类型的变量传递给方法时实际上传递的是该变量存储的值的副本即对象的引用。以下是这个过程的具体解释
按值传递的本质 传递的是值的副本。对于基本数据类型这个值就是数据本身对于引用数据类型这个值是对象的引用。
误解引用传递 有人可能会认为因为引用数据类型的参数可以改变原始对象的状态所以Java使用的是引用传递。
举例说明 假设有一个对象 Person p 存储在堆上其引用存储在栈上的变量 p 中。 当我们将 p 作为参数传递给方法时栈上会创建一个引用的副本。 方法内部使用这个副本引用来访问和修改堆上的对象。
示例代码
public class Person {String name;public Person(String name) {this.name name;}
}public void changeName(Person p) {p.name New Name;
}public static void main(String[] args) {Person person new Person(Original Name);changeName(person);// person.name 现在是 New Name
}在这个例子中changeName 方法接收了一个 Person 对象的引用。虽然看起来像是引用传递但实际上是按值传递了这个引用的副本。这个副本指向与原始引用相同的 Person 对象所以修改是通过引用副本进行的但影响到了原始对象。
总结 在Java中引用数据类型的参数传递是按值传递的传递的是对象引用的副本但由于这个副本和原始引用指向同一个对象所以看起来像是引用传递。
真正的引用传递pass by reference是指将变量的引用传递给方法或函数且按引用传递传递的不是值的副本而是实际的引用本身。
4. String是值传递还是引用传递
在Java中String是一种特殊的引用数据类型。对于String类型的参数传递可以认为是按值传递的但情况稍微有些复杂。
String的特殊性 String是不可变immutable的这意味着一旦创建了一个String对象其内容就不能改变。因此任何试图修改String对象内容的操作都会返回一个新的String对象。
参数传递行为 当我们将一个String对象作为参数传递给方法时传递的是该String对象的引用的副本。然而由于String是不可变的所以方法内部无法直接修改原始的String对象。任何修改操作都会创建一个新的String对象并将引用副本指向这个新对象。
示例
public class Main {public static void main(String[] args) {String original Hello;changeString(original);System.out.println(original); // 输出仍然是 Hello}public static void changeString(String str) {str World; // 这不会改变原始的String对象System.out.println(str); // 输出 World}
}在这个例子中changeString方法试图修改传入的String参数。然而由于String是不可变的str World;实际上是在栈上创建了一个新的String引用并将其指向一个新的String对象。原始的String对象仍然保持不变所以main方法中的original变量打印出来的仍然是Hello。
总结 虽然String的参数传递看起来像是引用传递但由于String是不可变的所以方法内部无法修改原始的String对象。因此在某种意义上String的参数传递可以被认为是按值传递。
5. 接口和抽象类的区别
相同点都是上层的抽象层不能被实例化都能包含抽象方法。不同点 抽象类可以包含非抽象方法而接口中只能包含抽象方法。抽象类可以包含普通和静态的成员变量接口只能包含 public static final 修饰的常量。一个类只能继承一个抽象类但可以实现多个接口。抽象类可以包含构造方法接口不能包含构造方法。
interface MyInterface {void method1(); // Abstract method
}abstract class MyAbstractClass {abstract void method2(); // Abstract methodvoid method3() {// Concrete method}
}6. 反射是在运行时获取类的相关信息。可以通过 Class 类来获取字段、方法等信息从而对类进行操作。
在Java中反射允许我们在运行时获取类的相关信息并且可以动态地操作类的字段、方法、构造函数等。通过使用Class类我们可以获取类的各种信息并对其进行操作。
下面是一个简单的示例来说明如何使用反射来获取类的信息
import java.lang.reflect.Field;
import java.lang.reflect.Method;public class ReflectionExample {public static void main(String[] args) throws Exception {Class? myClass Class.forName(com.example.MyClass); // 获取类的 Class 对象// 获取类的字段信息Field[] fields myClass.getDeclaredFields();for (Field field : fields) {System.out.println(field.getName());}// 获取类的方法信息Method[] methods myClass.getDeclaredMethods();for (Method method : methods) {System.out.println(method.getName());}// 创建类的实例并调用方法Object obj myClass.getDeclaredConstructor().newInstance();Method myMethod myClass.getDeclaredMethod(myMethod);myMethod.invoke(obj);}
}在这个示例中我们使用Class.forName方法来获取指定类的Class对象然后通过该对象可以获取类的字段和方法信息。我们还演示了如何通过反射来创建类的实例并调用其方法。
需要注意的是反射是一种功能强大但也复杂的机制应该谨慎使用。过度依赖反射会导致代码可读性和性能上的问题。因此在使用反射时需要权衡利弊并尽量避免滥用。
7. Java 中创建实例对象的初始化顺序
在Java中创建实例对象的初始化顺序通常按照以下步骤进行
父类的静态变量和静态代码块初始化子类的静态变量和静态代码块初始化父类的实例变量和代码块按照在类中的声明顺序初始化父类的构造函数子类的实例变量和代码块按照在类中的声明顺序初始化子类的构造函数
这个顺序确保了在创建对象时各个部分都能够按照正确的顺序得到初始化。这个过程对于理解Java对象创建和初始化非常重要特别是在涉及到继承关系和多态性的情况下。
8. 获取反射的几种方式
在Java中获取反射的几种方式包括使用Class.forName()、对象.getClass()和直接通过类名.class来获取Class对象。这些方式都可以用于获取反射对象并进行动态操作。 Class.forName() 使用Class.forName()方法可以根据类的全限定名包括包名来获取对应的Class对象。 Class clazz Class.forName(com.example.YourClass);对象.getClass() 在已经存在对象的情况下可以通过调用对象的getClass()方法来获取对应的Class对象。 YourClass obj new YourClass();
Class clazz obj.getClass();直接通过类名.class 可以直接通过类名后加上.class来获取对应的Class对象。 Class clazz YourClass.class;这些方式都可以用于获取Class对象然后通过Class对象进行反射操作例如创建对象、调用方法、访问字段等。根据具体的情况选择合适的方式来获取反射对象以便实现灵活的编程和动态的操作。
9. 类加载双亲委派模型
当然可以以下是完整的内容包括了9.1和9.2的部分
9什么是双亲委派模型
在双亲委派模型中类加载器收到类加载请求时会按照以下步骤操作 检查该类是否已被加载首先检查这个类是否已经被当前类加载器加载过如果已经加载过则直接返回已加载的类。 委派给父类加载器如果该类尚未被加载则当前类加载器会将请求委派给其父类加载器去处理。这一过程会一直上溯到启动类加载器Bootstrap Class Loader。 尝试加载只有当父类加载器无法完成这个类的加载请求时例如该类不在父类加载器的搜索路径中当前类加载器才会尝试自己加载这个类。
为什么叫双亲委派模型
这个模型被称为“双亲委派”是因为每个类加载器都有一个“父”类加载器它首先将加载请求委托给这个“父”加载器。这里的“双亲”并不是指生物学上的双亲而是指在类加载器层次结构中的“父辈”。
双亲委派模型的好处
双亲委派模型有以下几个好处
避免类的重复加载通过委派给父类加载器可以避免同一个类被不同的类加载器多次加载确保每个类在JVM中只存在一个副本。保证Java核心API不被篡改由于Bootstrap Class Loader是位于委派链的最顶端负责加载Java的核心类库因此可以确保这些核心类库不会被自定义的类加载器加载从而保护了Java核心API的安全性和稳定性。
如何打破双亲委派模型
在某些特殊情况下可能需要打破双亲委派模型。可以通过以下方式实现
自定义类加载器通过定义自己的类加载器并重写其loadClass方法可以改变委派逻辑实现自定义的类加载行为。场景举例例如在Java EE容器或者某些Web服务器如Tomcat中由于需要实现容器的隔离性或者热替换等特性会实现自己的类加载器并不完全遵循双亲委派模型。
综上所述双亲委派模型是Java类加载机制中的一个重要概念它通过委派的方式提高了类加载的效率和安全性但在特定场景下也可以根据需要进行适当的打破和调整。
public class ClassLoadingExample {public static void main(String[] args) {ClassLoadingExample example new ClassLoadingExample();ClassLoader appClassLoader ClassLoader.getSystemClassLoader();System.out.println(Application ClassLoader: appClassLoader);System.out.println(Extension ClassLoader: appClassLoader.getParent());System.out.println(Bootstrap ClassLoader: appClassLoader.getParent().getParent());}
}10. 在重写 equals 方法时通常需要重写 hashCode 方法以确保相等的对象具有相同的 hashCode 值避免在集合类中可能出现的问题。
在Java中如果一个类重写了equals方法以改变两个对象相等的定义通常也需要重写hashCode方法。以下是需要同时重写这两个方法的原因
10.1 equals和hashCode方法的关系
在Java中equals和hashCode方法之间有一个重要的契约这个契约在java.lang.Object类的文档中有详细的描述
如果两个对象根据equals(Object)方法返回的结果是相等的那么调用这两个对象各自的hashCode()方法必须返回相同的整数结果。如果两个对象根据equals(Object)方法返回的结果是不相等的那么调用这两个对象各自的hashCode()方法通常不是必须应该返回不同的整数结果。
10.2 为什么需要重写hashCode
一致性当对象的状态没有改变时多次调用同一个对象的hashCode()方法应该返回相同的值。相等对象必须有相同的hashCode如果两个对象相等即equals方法返回true它们必须有相同的hashCode值这是为了保证在使用哈希表如HashSet、HashMap等时这两个对象能够被存储在同一个桶bucket中。不相等对象应该有不同的hashCode虽然不是必须的但如果两个对象不相等它们有不同的hashCode值可以提高哈希表的性能因为这样可以减少哈希冲突的可能性。
10.3 不遵守契约的问题
如果不遵守这个契约在集合类如HashSet、HashMap等中可能会出现以下问题
如果两个相等的对象具有不同的hashCode值那么在哈希表中它们可能会被存储在不同的桶中这将导致equals方法不会被调用从而无法正确处理这两个对象例如无法删除其中一个对象。如果多个不相等的对象具有相同的hashCode值那么它们都会被映射到同一个桶中这将增加哈希表的冲突率导致性能下降。
10.4 如何正确重写hashCode
为了遵守上述契约重写hashCode方法时以下是一些通用的指导原则
确保相同的对象总是返回相同的hashCode值。确保如果两个对象根据equals方法相等它们也必须有相同的hashCode值。尽量使不相等的对象的hashCode值不同以减少哈希冲突。通常hashCode方法会基于对象中用于确定相等性的所有字段来计算哈希值并且计算方式应该尽可能简单和高效。很多IDE都提供了自动生成hashCode和equals方法的功能这通常是一个安全且高效的方式。
public class Person {private String name;private int age;Overridepublic boolean equals(Object obj) {if (this obj) return true;if (obj null || getClass() ! obj.getClass()) return false;Person person (Person) obj;return age person.age Objects.equals(name, person.name);}Overridepublic int hashCode() {return Objects.hash(name, age);}
}11. 面向对象的特征
- 封装将对象的属性和行为封装在一起隐藏内部细节提供对外的接口。
- 继承允许子类继承父类的属性和方法实现代码复用。
- 多态同一方法在不同对象上表现出不同的行为。当然可以下面是带有英文缩写的设计原则
1. 抽象
过程抽象将复杂的操作或行为抽象为一个简单的函数或方法调用。数据抽象定义数据类型和可以对这些类型执行的操作而无需关心数据的具体表示。
2. 封装
封装Encapsulation隐藏对象的内部细节仅对外暴露需要公开的接口。这有助于保护对象的状态不被外部干扰和不恰当的使用。
3. 继承
继承Inheritance允许某个类子类继承另一个类父类的属性和方法实现代码重用并添加新功能。
4. 多态
多态Polymorphism允许不同类的对象对同一消息做出响应实现同一操作通过不同对象执行不同行为。
扩展的特征和设计原则
单一职责原则SRP - Single Responsibility Principle一个类应该只有一个改变的理由。开放封闭原则OCP - Open/Closed Principle软件实体应该对扩展开放对修改封闭。里氏替换原则LSP - Liskov Substitution Principle子类必须能够替换其父类在程序中的任何位置。合成/聚合原则C/A - Composition/Aggregation Principle优先使用对象组合而不是类继承。迪米特法则LoD - Law of Demeter一个对象应当对其他对象有尽可能少的了解。
12. StringBuffer 和 StringBuilder 的区别
StringBuffer 是线程安全的使用 synchronized 关键字来保证线程安全效率较低。StringBuilder 是非线程安全的效率较高。
public class StringBuilderExample {public static void main(String[] args) {StringBuilder sb new StringBuilder(Hello);sb.append( World);System.out.println(sb.toString());}
}13. 浅拷贝和深拷贝
- 浅拷贝复制对象时只复制对象本身和其内部基本类型属性的值而不复制引用类型属性指向的对象。
- 深拷贝复制对象时会递归地复制所有引用类型属性指向的对象使得新对象和原对象完全独立。public class DeepCopyExample {public static void main(String[] args) {ListString list1 new ArrayList();list1.add(item1);list1.add(item2);// Shallow copyListString list2 new ArrayList(list1);list2.add(item3);// Deep copyListString list3 new ArrayList(list1.size());for (String item : list1) {list3.add(new String(item));}list3.add(item4);System.out.println(List1: list1);System.out.println(List2 (Shallow Copy): list2);System.out.println(List3 (Deep Copy): list3);}
}