网站首页 包含标签 数据库 的所有文章

  • Java String面试题汇总

    文章目录 Java中String是关键字吗? 为什么String是不可变的? 什么是字符串常量池? 什么是String intern()? 如何使用正则表达式查找匹配的字符串? 如何比较两个字符串? Java中的字符串是如何工作的? 如何检查回文字符串? 如何在字符串中删除或替换字符? 如何将字符串转换为小写或大写? 我们可以在’ switch ‘语句中使用String吗? 如何打印一个字符串的所有排列组合? 如何翻转字符串中的每个单词? 如何分割一个字符串? 为什么不应该使用字符串来存储密码? 字符串是线程安全的吗? 为什么String是HashMap键的最佳选择? String、StringBuffer和StringBuilder之间有什么区别? 如何连接多个字符串? 统计给定程序中的字符串对象数量 统计字符串中每个字符的出现次数? 在不使用 StringBuilder 或 StringBuffer 的情况下反转字符串? 总结 给定的Java String面试问题涵盖了字符串方法、字符串不可变性和内存泄漏问题,以及简单的示例和用例。我将尽力涵盖Java面试中最常问的String类相关问题。 Java中String是关键字吗? 不是的。String不是Java中的保留关键字。它是一种派生数据类型,也就是一个类。 public class StringExample { public static void main(String[] args) { Integer String = 10; System.out.println(String); //输出 10 } } 为什么String是不可变的? 第一个原因是性能改进。Java语言的开发旨在加快应用程序的开发速度,因为在之前的语言中它的速度不是那么快。JVM的设计者必须足够聪明,能够识别到实际应用程序将主要由字符串组成,这些字符串是标签、消息、配置、输出和其他形式的数据。看到这种过度使用,他们想象了字符串的不当使用可能有多危险。因此,他们提出了字符串池的概念。字符串池实际上是一组大部分是唯一的字符串。字符串池背后的基本思想是一旦创建了字符串,就可以重用它。这样,如果在代码中创建了一个特定的字符串20次,应用程序只有一个实例。 我认为第二个原因是安全性考虑。字符串是Java编程的各个方面中最常用的参数类型。无论是加载驱动程序还是打开URL连接,都需要将信息作为字符串参数传递。如果字符串不是不可变的,就会引发一系列安全性问题。 什么是字符串常量池? 字符串常量池是常规堆内存中的一块特定内存区域,用于存储这些字符串常量。在应用程序的生命周期中,字符串变量引用这些常量。 在Java中,我们可以通过多种方式创建字符串。例如,使用字符串字面值和使用new关键字。 String str1 = "abc"; String str2 = new String("abc"); 使用字符串字面值会导致JVM验证是否已经存在一个与字符串”abc”(相同的字符序列)。如果存在这样的字符串,JVM会将现有对象的引用赋给变量str;否则,将创建一个新的对象”abc”,并将其引用赋给变量str1。 当使用new关键字时,Java会在正常的堆内存中创建一个新的String对象。我们必须使用intern方法将字符串从堆内存移到字符串池中。 为了更好地利用内存并提高性能,建议使用字符串字面值来创建字符串。除非需要显式复制原始字符串,否则不需要使用构造函数,因为字符串是不可变的。 什么是String intern()? 当调用String的intern()方法时,如果字符串池已经包含与此字符串相同内容的字符串(由equals()方法确定),则返回池中的引用。否则,将此String对象添加到池中,并返回池中此新String对象的引用。 简单来说,字符串池化是将一个String对象从常规堆内存移动到字符串常量池中,并使用池中的对象引用的过程。 String str = new String("abc"); str.intern(); 对于intern()方法,对于任何两个字符串s1和s2,只有当s1.equals(s2)为true时,s1.intern() == s2.intern()才为true。 这意味着如果s1和s2是不同的字符串对象,并且具有相同的字符序列,那么在两者上调用intern()将导致由两个变量引用的单个字符串池字面值。 默认情况下,请记住所有字面字符串和具有字符串值的常量表达式都会被池化。 如何使用正则表达式查找匹配的字符串? 我们可以使用Pattern和Matcher API进行正则表达式匹配。String类提供了自己的matches()方法。我们应该直接使用matches()方法。该方法在函数定义内部也使用Pattern.matches()。 String str = new String("Welcome to panziye.com"); System.out.println(str.matches("(.*)ziye(.*)")); //Prints true System.out.println(Str.matches("(.*)www(.*)")); //Print false 如何比较两个字符串? 面试中的另一个受欢迎的领域。通常有两种比较对象的方法: 使用==运算符 使用equals()方法 ==运算符用于比较对象引用。因此,如果两个字符串对象引用字符串池中的相同字面值或堆中的相同字符串对象,则’s == t’将返回true,否则返回false。 equals()方法在String类中被重写,并验证String对象所持有的字符序列。换句话说,equals()方法比较字符串对象的值。如果它们存储相同的字符序列,’s.equals(t)’将返回true,否则返回false。 Java中的字符串是如何工作的? 与其他编程语言一样,Java中的字符串是字符序列。这更像是一个用于处理该字符序列的实用类。这个字符序列以char数组类型的以下变量维护: /** 值使用char 数组存储 */ private final char value[]; 各种字符串方法在不同的情况下使用以下变量来维护数组中的位置: /** 偏移量(offset)是存储中首个被使用的索引。*/ private final int offset; /** 计数(count)是字符串中字符的数量。 */ private final int count; 如何检查回文字符串? 如果一个字符串的值在反转后仍然相同,那么它就是回文字符串。要检查回文字符串,可以将字符串反转,然后将其与原始字符串进行比较。 如果原始字符串和反转后的字符串相同,那么给定的字符串就是回文字符串。 String originalString = "abcdcba"; StringBuilder strBuilder = new StringBuilder(originalString); String reverseString = strBuilder.reverse().toString(); boolean isPalindrame = originalString.equals(reverseString); System.out.println(isPalindrame); //true 如何在字符串中删除或替换字符? 要替换或删除字符,可以使用String.replace()或String.replaceAll()方法。 这两种方法都接受两个参数。第一个参数是要替换的字符,第二个参数是要放入字符串中的新字符。 如果想删除字符,可以在第二个参数中传递空字符。 String originalString = "howtodoinjava"; //Replace one character System.out.println( originalString.replace("h", "H") ); //Howtodoinjava //Replace all matching characters System.out.println( originalString.replaceAll("o", "O") ); //hOwtOdOinjava //Remove one character System.out.println( originalString.replace("h", "") ); //owtodoinjava //Remove all matching characters System.out.println( originalString.replace("o", "") ); //hwtdinjava 如何将字符串转换为小写或大写? 要将字符串转换为小写或大写,可以使用String.toLowerCase()和String.toUpperCase()方法。 String blogName = "PanZiYe.com"; System.out.println(blogName.toLowerCase()); //panziye.com System.out.println(blogName.toUpperCase()); //PANZIYE.COM 我们可以在’ switch ‘语句中使用String吗? 可以,自Java 7以来,我们可以在switch语句中使用String类。在Java 7之前,这是不可以的,我们必须使用if-else语句来实现类似的功能。 如何打印一个字符串的所有排列组合? 排列是将字符元素重新排列,以使每个排列在其他排列方面都是唯一的。例如,下面是字符串“ABC”的排列 – ABC、ACB、BAC、BCA、CBA 和 CAB。 长度为N的字符串有N!(N阶乘)个排列。 import java.util.HashSet; import java.util.Set; public class StringExample { public static void main(String[] args) { System.out.println(getPermutations("ABC")); //Prints //[ACB, BCA, ABC, CBA, BAC, CAB] } public static Set<String> getPermutations(String string) { //All permutations Set<String> permutationsSet = new HashSet<String>(); // invalid strings if (string == null || string.length() == 0) { permutationsSet.add(""); } else { //First character in String char initial = string.charAt(0); //Full string without first character String rem = string.substring(1); //Recursive call Set<String> wordSet = getPermutations(rem); for (String word : wordSet) { for (int i = 0; i <= word.length(); i++) { permutationsSet.add(charInsertAt(word, initial, i)); } } } return permutationsSet; } public static String charInsertAt(String str, char c, int position) { String begin = str.substring(0, position); String end = str.substring(position); return begin + c + end; } } 如何翻转字符串中的每个单词? 要分别翻转每个单词,首先要对字符串进行分词,将所有单词分隔成一个数组。然后,对每个单词应用翻转单词的逻辑,最后将所有单词连接起来。具体代码可以参考:反转字符串中的单词 如何分割一个字符串? 使用String.split()方法,该方法会根据给定的正则表达式来分割字符串。它也被称为根据分隔符获取字符串标记。 split()方法返回一个字符串数组,数组中的每个字符串都是一个独立的标记。 为什么不应该使用字符串来存储密码? 我们知道在Java中,字符串存储在常量池中。一旦一个字符串被创建在字符串池中,它会一直在池中存在,除非被垃圾回收。在这段时间内,任何恶意程序都可以访问物理内存中的内存区域并访问字符串数据。 如果我们将密码存储为一个字符串,它将在内存中保留的时间比所需的时间长,因为垃圾收集周期是不可预测的。这会使敏感的密码字符串容易受到黑客攻击和数据盗窃的威胁。 有人可能会提出一个观点,即在使用后将字符串设置为空。但是,这是不可行的,因为我们知道一旦一个字符串被创建,就无法操纵它,例如无法更改它的内容。字符串是不可变的。 但是,字符数组是可变的,我们可以在使用后覆盖它们的内容。因此,我们的应用程序应该使用char[]来存储密码文本,并在使用密码后替换数组内容为空白。 总之,使用char[]而不是字符串来存储密码是更安全的做法,因为它可以在使用后覆盖密码,减少了敏感信息被恶意访问的风险。这是更好的密码安全实践。 String password = "123456"; //不能使用字符串 char[] passwordChars = new char[4]; //从数据库等系统中获取密码。 //使用密码 for(char c : passwordChars) { c = ' '; } 字符串是线程安全的吗? 是的,字符串是线程安全的,因为它们是不可变的。 请记住,所有不可变的实例都是线程安全的,这是它们的设计特点。 为什么String是HashMap键的最佳选择? 在Java中,Map的键必须是不可变的,并且应该遵守equals()和hashCode()方法之间的约定。String类满足这两个条件。 此外,String类提供了许多有用的方法,用于比较、排序、分词或大小写转换。在对Map执行CRUD操作时,可以使用这些方法。 所有这些特点使得String类非常适合用作Map中的键,而不是创建我们自己的类。 String、StringBuffer和StringBuilder之间有什么区别? String类表示字符序列,并提供了处理字符的有用方法。String类的实例是不可变的。因此,每次我们使用String类进行字符串连接时,都会创建一个新对象,其中包含连接后的字符串。 StringBuilder类用于以更节省内存的方式执行字符串连接操作。它在内部维护char[],只在这个数组中操作内容。当我们在执行所有操作后需要获取完整的连接字符串时,它会使用存储的字符数组创建一个新的String。 StringBuffer与StringBuilder类非常相似,唯一的区别是它是线程安全的,它的所有方法都是同步的。 如何连接多个字符串? 根据是否需要线程安全,使用StringBuffer或StringBuilder类。在这两个类中,使用append()方法来连接字符串。 统计给定程序中的字符串对象数量 String s1 = "panziye.com"; String s2 = "panziye.com"; String s3 = new String("panziye.com"); 上面的代码将创建2个对象。 第一条语句将在字符串池中创建第一个字符串字面量。 第二条语句不会创建任何新对象,s2将引用与s1相同的字符串常量。 第三条语句将在堆内存中创建一个新的字符串对象。 统计字符串中每个字符的出现次数? 要找出给定字符串中每个字符的出现次数,我们使用了HashMap,其中字符作为键,其出现次数作为值。 每次字符出现新的次数时,我们将增加该字符的计数值。代码实现可以参考《Java中查找字符串中的重复字符》 在不使用 StringBuilder 或 StringBuffer 的情况下反转字符串? 反转字符串的最佳方法是使用 StringBuffer.reverse() 和 StringBuilder.reverse() 方法。尽管如此,面试官可能会要求您编写自己的程序,以检查您的技能水平。 请使用下面给出的基于递归的示例来反转字符串。 该程序从字符串中取出第一个字符,并将其放置在字符串的最后位置。它使用这种替换方式来反转字符串中的所有字符,直到整个字符串被反转。 具体代码实现可以参考:《基于递归的示例来反转字符串》 总结 以上就是我认为面试时经常被问到的关于String类问题,希望对你的面试有帮助。如果你知道更多关于Java String面试题问题,可以留言。 ...

    2023-09-16 174
  • Java OOP面向对象编程简介及4大特性

    文章目录 1. 什么是OOP或面向对象编程? 1.1. 类和对象 1.2. 构造函数 2. OOP 的 4 个特性 2.1. 抽象 2.1.1. 数据抽象 2.1.2. 控制抽象 2.2. 封装 2.2.1. 信息隐藏 2.2.2. 实施隐藏 2.3. 继承 2.3.1. 继承示例 2.3.2. 继承的类型 2.4. 多态性 2.4.1. 编译时多态性 2.4.2. 运行时多态性 3.更多面向对象编程概念 3.1. 耦合度 3.2. 内聚度 3.3. 关联 3.4. 聚合 3.5. 组合 4.最佳实践 4.1. 优先考虑组合而不是继承 4.2. 面向接口编程,而不是具体实现 4.3. DRY(不要重复自己) 4.4. 封装变化 4.5. 单一职责原则 4.6. 开闭原则 5. 总结 面向对象编程(OOP)是指基于对象的编程方法,而不是像函数式编程那样仅基于函数和过程。这些对象可以包含数据(属性)和方法(行为),就像我们在应用程序中建模的现实生活实体一样。 本教程将教我们四个主要特性——抽象、封装、继承和多态性。这些也被称为面向对象编程范式的四大特性。 1. 什么是OOP或面向对象编程? 早期,人们用二进制代码编写程序,并使用机械开关来加载程序。后来,随着硬件功能的发展,专家尝试使用高级语言来简化编程,我们使用编译器从程序生成机器指令。 随着更多的发展,专家们创建了基于小函数的结构化编程。这些函数在很多方面都有帮助,例如代码重用、局部变量、代码调试和代码可维护性。 随着计算的进步和对更复杂应用程序的需求,结构化编程的局限性开始显现出来。复杂的应用程序需要与现实世界和用例更紧密地建模。 后来,专家们开发了面向对象编程。在 OOP 的中心,我们有对象和类。就像现实生活中的实体一样,对象具有两个重要特征: 数据– 讲述属性和对象的状态 行为– 赋予其改变自身并与其他对象进行通信的能力 1.1. 类和对象 对象是类的实例。每个对象都有自己的状态、行为和身份。类是其对象的蓝图或模板。 对象可以通过调用函数与其他对象进行通信。它有时被称为消息传递。 例如,如果我们正在开发人力资源应用程序,那么它由实体/参与者组成,例如员工、经理、部门、工资单、假期、目标、时间跟踪等。为了在计算机程序中对这些实体进行建模,我们可以创建具有类似属性的类现实生活中的数据属性和行为。 例如,员工实体可以表示为Employee类: public class Employee { private long id; private String title; private String firstName; private String middleName; private String lastName; private Date dateOfBirth; private Address mailingAddress; private Address permanentAddress; // 根据应用程序的要求,可以添加更多类似的属性、getter和setter方法。 }   以上Employee作为模板。我们可以使用此类在应用程序中创建所需数量的不同员工对象。 Employee e = new Employee(111); e.setFirstName("Alex"); .. .. int age = e.getAge();   该id字段有助于存储和检索任何单个员工的详细信息。 对象标识通常由应用程序运行时环境维护,例如,用于Java应用程序的Java虚拟机(JVM)。每次我们创建一个 Java 对象时,JVM 都会为该对象创建一个哈希码并分配它。这样,即使程序员忘记添加id字段,JVM 也能确保所有对象都是唯一标识的。 1.2. 构造函数 构造函数是没有任何返回值的特殊方法。它们的名称始终与类的名称相同,但它们可以接受参数,这些参数有助于在应用程序开始使用对象之前设置对象的初始状态。 如果我们不提供任何构造函数,JVM 会为该类分配一个默认构造函数。此默认构造函数不接受任何参数,即无参构造。 请记住,如果我们为任何类分配构造函数,那么 JVM 不会为其分配默认构造函数。如果需要,我们需要向类显式指定默认构造函数。 public class Employee { // 默认构造 public Employee() { } // 自定义构造 public Employee(int id) { this.id = id; } }   2. OOP 的 4 个特性 面向对象编程的四大特点是: 抽象 封装 继承 多态 2.1. 抽象 当我们将抽象与实时示例联系起来时,它就很容易理解。例如,当我们驾驶汽车时,我们不必关心汽车的确切内部工作原理。我们关心的是通过方向盘、制动踏板、油门踏板等接口与汽车进行交互。在这里,我们对汽车的了解是抽象的。 在计算机科学中,抽象是用形式与其含义(语义)相似的表示来定义数据和程序,同时隐藏实现细节的过程。 简而言之,抽象隐藏了与上下文无关的信息,或者只显示相关信息,并通过将其与现实世界中的类似信息进行比较来简化它。 通常抽象可以通过两种方式来看待: 2.1.1. 数据抽象 数据抽象是从多个较小的数据类型创建复杂数据类型的方法,这更接近现实生活中的实体。例如, Employee 类可以是具有各种小关联的复杂对象。 public class Employee { private Department department; private Address address; private Education education; //So on... } 因此,如果您想获取有关员工的信息,您可以从 Employee 对象中询问 – 就像在现实生活中一样,询问该人本人。 2.1.2. 控制抽象 控制抽象是通过隐藏复杂任务的操作序列(在简单的方法调用内)来实现的,因此执行任务的逻辑可以对客户端隐藏,并且可以在不影响客户端代码的情况下进行更改。 public class EmployeeManager { public Address getPrefferedAddress(Employee e) { //从数据库获取所有地址 //在这里实现获取所有地址的逻辑 //返回一个包含所有地址的列表 } } 在上面的例子中,明天如果你想改变逻辑,让每次国内地址始终是首选地址,你就改变getPrefferedAddress()方法内部的逻辑,  客户端将不受影响。 2.2. 封装 将数据和方法包装在类中并与实现隐藏(通过访问控制)相结合通常称为封装。结果是具有特征和行为的数据类型。 “无论发生何种变化,都要将其封装起来” – 一条著名的设计原则。 封装本质上既有信息隐藏,也有实现隐藏。 信息隐藏是通过使用访问控制修饰符(public、private、protected)来完成的,实现隐藏是通过为类创建接口来实现的。 实现隐藏允许设计者修改对象履行职责的方式。当设计(甚至需求)可能发生变化时,这一点尤其有价值。 让我们举一个例子来更清楚地说明这一点。 2.2.1. 信息隐藏 class InformationHiding { //限制直接访问内部数据 private ArrayList items = new ArrayList(); //提供一种访问数据的方式 - 内部逻辑可以在将来安全地进行更改 public ArrayList getItems(){ return items; } }   2.2.2. 实施隐藏 interface ImplemenatationHiding { Integer sumAllItems(ArrayList items); } class InformationHiding implements ImplemenatationHiding{//限制直接访问内部数据private ArrayList items = new ArrayList(); //提供一种访问数据的方式 - 内部逻辑可以在将来安全地进行更改 public ArrayList getItems(){ return items; } public Integer sumAllItems(ArrayList items) { // 在这里,您可以按任何顺序执行N项操作 // 这些操作您不希望客户端知道 // 您可以更改顺序甚至整个逻辑 // 而不会影响客户端。 } }   2.3. 继承 继承是面向对象编程中的另一个重要概念。继承是一个类获取父类的属性和行为的一种机制。它本质上是在类之间创建父子关系。在Java中,我们使用继承主要是为了代码的可重用性和可维护性。 Java 中的关键字“ extends ”用于继承类。“ extends”关键字表示我们正在创建一个派生自现有类的新类。 在Java术语中,被继承的类称为超类。新类称为子类。 子类从其超类继承所有非私有成员(字段、方法和嵌套类)。构造函数不是成员,因此不能被子类继承,但可以从子类调用超类的构造函数。 2.3.1. 继承示例 public class Employee { private Department department; private Address address; private Education education; //So on... } public class Manager extends Employee { private List<Employee> reportees;}   在上面的代码中,Manager 是 Employee 的特殊版本,重用Employee类中的部门、地址和教育,并定义了自己的reportees列表。 2.3.2. 继承的类型 单继承——子类派生自一个父类。 class Parent { //code } class Child extends Parent { //code}   多重继承——一个孩子可以从多个父母那里继承。在 JDK 1.7 之前,通过使用类在 java 中无法实现多重继承。但从 JDK 1.8 开始,通过使用带有默认方法的接口可以实现多重继承。 interface MyInterface1 { } interface MyInterface2 { } class MyClass implements MyInterface1, MyInterface2 { }   多级继承——指三个以上的类之间的继承,其中一个子类将充当另一个子类的父类。 class A { } class B extends A { } class C extends B { }   层次继承是指有一个超类和多个扩展超类的子类时的继承。 class A { } class B extends A { } class C extends A { } class D extends A { }   混合继承——是两种或多种继承类型的组合。因此,当类之间的关系包含两种或多种类型的继承时,我们就说类实现了混合继承。 interface A { } interface B extends A { } class C implements A { } class D extends C impements B { }   2.4. 多态性 多态性是一种能力,通过它,我们可以创建在不同的编程上下文中表现不同的函数或引用变量。它通常被称为具有多种形式的一个名称。 例如,在大多数编程语言中, '+' 运算符用于添加两个数字和连接两个字符串。根据变量的类型,运算符会更改其行为。这称为运算符重载。 在Java中,多态本质上分为两种: 2.4.1. 编译时多态性 在编译时多态性中,编译器可以在编译时将适当的方法绑定到相应的对象,因为它拥有所有必要的信息并且知道在程序编译期间调用哪个方法。 它通常被称为静态绑定或早期绑定。 在Java中,它是通过使用方法重载来实现的。在方法重载中,方法参数可以随参数的数量、顺序或类型而变化。 class PlusOperator { int sum(int x, int y) { return x + y; } double sum(double x, double y) { return x + y; } String sum(String s1, String s2) { return s1.concat(s2); } }   2.4.2. 运行时多态性 在运行时多态性中,对重写方法的调用在运行时动态解析。将在其上执行方法的对象是在运行时确定的——通常取决于用户驱动的上下文。 它通常被称为动态绑定或方法重写。我们可能听说过动态方法调度这个名字。 在运行时多态性中,我们通常有一个父类和至少一个子类。在类中,我们编写一条语句来执行父类和子类中存在的方法。 使用父类类型的变量给出方法调用。类的实际实例是在运行时确定的,因为父类类型变量也可以存储对父类以及子类实例的引用。 class Animal { public void sound() { System.out.println("Some sound"); } } class Lion extends Animal {public void sound() {System.out.println("Roar");}} class Main {public static void main(String[] args) { //Parent class reference is pointing to a parent object Animal animal = new Animal(); animal.sound(); //Some sound //Parent class reference is pointing to a child object Animal animal = new Lion(); animal.sound(); //Roar }}   3.更多面向对象编程概念 除了上述的四个面向对象编程构建基块之外,还有一些其他概念在构建整体理解方面起着重要作用。 在深入探讨之前,我们需要了解术语“模块”。在一般的编程中,模块是执行独特功能的类或子应用程序。在人力资源应用程序中,一个类可以执行诸如发送电子邮件、生成工资单、计算员工年龄等各种功能。 3.1. 耦合度 耦合度是模块之间相互依赖程度的度量。耦合度指的是软件元素与其他元素之间的联系强度。良好的软件将具有低耦合度。 这意味着一个类应该执行唯一的任务,或者只执行与其他任务无关的任务。例如,一个EmailValidator类只会验证电子邮件。同样,EmailSender类只会发送电子邮件。 如果我们将这两个功能都包含在一个名为EmailUtils的单个类中,那么这就是紧密耦合的示例。 3.2. 内聚度 内聚度是保持模块整体性的内在因素。良好的软件设计将具有高内聚度。 这意味着一个类/模块应该包含执行其功能所需的所有信息,而不依赖于其他内容。例如,一个EmailSender类应该能够配置SMTP服务器,并接受发件人的电子邮件、主题和内容。基本上,它应该只关注发送电子邮件。 应用程序不应该将EmailSender用于发送电子邮件以外的任何其他功能。低内聚度会导致庞大的类,难以维护、理解和降低可重用性。 3.3. 关联 关联指的是具有独立生命周期且彼此没有所有权的对象之间的关系。 以教师和学生为例。多个学生可以与单个教师关联,单个学生也可以与多个教师关联,但它们都有自己的生命周期。 两者都可以独立创建和删除,因此当教师离开学校时,我们不需要删除任何学生,当学生离开学校时,我们也不需要删除任何教师。 3.4. 聚合 聚合指的是具有独立生命周期但具有“拥有关系”的对象之间的关系。它发生在子类和父类之间,其中子对象不能属于另一个父对象。 以手机和手机电池为例。一块电池一次只能属于一个手机。如果手机停止工作,我们从数据库中删除它,手机电池将不会被删除,因为它可能仍然可用。因此,在聚合中,虽然存在所有权,但对象有自己的生命周期。 3.5. 组合 组合指的是对象没有独立生命周期的关系。如果删除父对象,所有子对象都将被删除。 例如,问题和答案之间的关系。一个问题可以有多个答案,但答案不能属于多个问题。如果我们删除一个问题,它的所有答案将自动被删除。 4.最佳实践 4.1. 优先考虑组合而不是继承 继承和组合都促进了代码的可重用性。但在继承与组合之间,更倾向于使用组合。 组合的实现通常从创建各种表示系统必须展现的行为的接口开始。接口使得多态行为成为可能。实现已确定接口的类会根据需要构建并添加到业务领域类中。因此,系统行为可以在没有继承的情况下实现。 interface Printable { print(); } interface Convertible { print(); } class HtmlReport implements Printable, Convertible { } class PdfReport implements Printable { } class XmlReport implements Convertible { } 4.2. 面向接口编程,而不是具体实现 这会导致灵活的代码,可以与接口的任何新实现一起使用。我们应该将接口用作变量、方法的返回类型或方法的参数类型。 接口充当超类类型。通过这种方式,我们可以在不修改现有代码的情况下,在将来创建更多接口的特定实现。 4.3. DRY(不要重复自己) 不要编写重复的代码,而是使用抽象将常见的内容抽象到一个地方。 作为一个基本原则,如果在两个地方写了相同的代码 – 考虑将其提取到一个单独的函数中,并在两个地方调用该函数。 4.4. 封装变化 所有软件都会随着时间的推移而发生变化。因此,将您期望或怀疑将来会发生变化的代码封装起来。 在Java中,使用私有方法来隐藏此类实现,以使客户端不必在进行更改时更改其代码。 还建议使用设计模式来实现封装。例如,工厂设计模式封装了对象创建代码,并提供了灵活性,以后可以引入新类型,而不会影响现有客户端。 4.5. 单一职责原则 这是面向对象编程类设计的SOLID原则之一。它强调一个类应该只有一个责任。 换句话说,我们应该为一个目的编写、更改和维护一个类。这将使我们能够在不担心更改对另一个实体产生影响的情况下进行未来更改。 4.6. 开闭原则 它强调软件组件应该对扩展开放,但对修改关闭。 这意味着我们的类应该设计得当,以便其他开发人员在应用程序中的特定条件下更改控制流时,他们只需要扩展我们的类并重写一些函数,就可以了。 如果其他开发人员由于我们的类所施加的约束而无法设计所需的行为,那么我们应该重新考虑更改我们的类。 在整个面向对象编程范 Paradigm 内还有许多其他概念和定义,我们将在其他教程中学习。 5. 总结 这个Java面向对象编程(OOP)教程讨论了Java中的四个主要OOP特性,附有易于理解的程序和片段。您可以在评论部分提出您的问题。   ...

    2023-08-26 223
  • MySQL数据库基本操作

    基本操作有:查看有哪些数据库、查看有哪些表、创建数据库、创建表、查看表信息、向表中插入数据等 # 查看有哪些数据库 MariaDB [(none)]> show databases; # 切换到test数据库 MariaDB [(none)]> use test; # 查看当前数据库有哪些表 MariaDB [test]> show tables; Empty set (0.000 sec) # 表明当前数据库是空的 # 如果test数据库不存在,则创建 MariaDB [test]> CREATE DATABASE IF NOT EXISTS test; # 在数据库test种创建表三个表:books、authors、series MariaDB [test]> CREATE TABLE IF NOT EXISTS books ( # 创建books表(前提是books表不存在,如果已经存在,则不创建) -> BookID INT NOT NULL PRIMARY KEY AUTO_INCREMENT, -> Title VARCHAR(100) NOT NULL, -> SeriesID INT, AuthorID INT); Query OK, 0 rows affected (0.033 sec) MariaDB [test]> CREATE TABLE IF NOT EXISTS authors # 创建authors表(前提是authors表不存在,如果已经存在,则不创建) -> (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT); Query OK, 0 rows affected (0.005 sec) MariaDB [test]> CREATE TABLE IF NOT EXISTS series # 创建series表(前提是series表不存在,如果已经存在,则不创建) -> (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT); Query OK, 0 rows affected (0.005 sec) # 接下来我们再来看看表是否添加成功 MariaDB [test]> show tables; +----------------+ | Tables_in_test | +----------------+ | authors | | books | | series | +----------------+ 3 rows in set (0.000 sec) # 向books表中插入数据 MariaDB [test]> INSERT INTO books (Title,SeriesID,AuthorID) -> VALUES('The Fellowship of the Ring',1,1), -> ('The Two Towers',1,1), ('The Return of the King',1,1), -> ('The Sum of All Men',2,2), ('Brotherhood of the Wolf',2,2), -> ('Wizardborn',2,2), ('The Hobbbit',0,1); Query OK, 7 rows affected (0.004 sec) Records: 7 Duplicates: 0 Warnings: 0 # 查看表信息 MariaDB [test]> describe books; +----------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------+--------------+------+-----+---------+----------------+ | BookID | int(11) | NO | PRI | NULL | auto_increment | | Title | varchar(100) | NO | | NULL | | | SeriesID | int(11) | YES | | NULL | | | AuthorID | int(11) | YES | | NULL | | +----------+--------------+------+-----+---------+----------------+ 4 rows in set (0.002 sec) # 查询数据(从表中查询数据) MariaDB [test]> select * from books; +--------+----------------------------+----------+----------+ | BookID | Title | SeriesID | AuthorID | +--------+----------------------------+----------+----------+ | 1 | The Fellowship of the Ring | 1 | 1 | | 2 | The Two Towers | 1 | 1 | | 3 | The Return of the King | 1 | 1 | | 4 | The Sum of All Men | 2 | 2 | | 5 | Brotherhood of the Wolf | 2 | 2 | | 6 | Wizardborn | 2 | 2 | | 7 | The Hobbbit | 0 | 1 | +--------+----------------------------+----------+----------+ 7 rows in set (0.000 sec) 小知识:sql语句允许换行,直到遇到分号+回车才会认为sql语句输入结束,进入执行阶段 ...

    2023-08-12 206
  • 实战用手机拿下代刷网数据库

    由于最近太无聊,就想去康康别人的数据库咋样不过也是拿中小型的开刀,大型的就算了。 准备工具:手机,MT管理器,大马,站长之家,其余的自想,可以带个图片马 顺便找个代刷网 他的域名加上admin康康,嗯。。。 之后用站长之家,查出它的IP 之后我们尝试访问它的主机控制台特别强调本贴针对虚拟主机搭建的东西 众所周知,只要加个端口3312和vhost就可以进入控制面板登录界面 然后咱们康康啊,试着访问这个服务器安装脚本的后台,进不去昂之后操作来重点了,如果需要安装的源码,那么就是加个install,这位小伙子没有警觉性,都不删这个的 试着访问它下方提示的,结果有安装锁 然后试着访问它填的数据库,都知道是config.php,然后咱们康康,结果一片空白 之后就得换方法了咱们康康,诶,这么访问就出来了然后他填了数据库信息,太不谨慎了,这个文件不填也可以安装的咱们试着登录他的主机面板填好,登录,嗯哼 然后不要动里面的源码,先改一下数据库密码。 再偷偷放上个大马玩玩 额。。。好吧其实这篇文章是我闲得慌胡编乱造,半真半假。。。 ...

    2020-03-18 4928

联系我们

在线咨询:点击这里给我发消息

QQ交流群:KirinBlog

工作日:8:00-23:00,节假日休息

扫码关注