当前位置: 首页 > news >正文

Java 新手教程!面向对象设计一口气讲完![]~( ̄▽ ̄)~*(中)

目录

Java 内部类

Java面向对象的设计 - Java 内部类

什么是内部类?

例子

使用内部类的优点

访问局部变量的限制

内部类和继承

内部类中没有静态成员

生成的内部类的类文件

静态上下文中的内类

Java 内部类类型

Java面向对象设计 - Java内部类类型

成员内部类

局部内部类

例子

匿名内部类

例2

Java 静态对象类

Java面向对象设计 - Java静态内部类

例子

注意

例2

Java 内部类对象

Java面向对象设计 - Java内部类对象

语法

例子

Java 内部类成员

Java面向对象设计 - Java内部类成员

例子

在内部类中使用关键字this

隐藏变量

从外部类引用变量

Java 继承

Java面向对象设计 - Java继承

语法

例子

注意

对象父类

向上转换和向下转换

instanceof运算符

禁用继承

Java 方法重写

Java面向对象设计 - Java方法重写

方法重写

例子

注意

访问级别

访问重写方法

Java 继承和构造函数

Java面向对象设计 - Java继承和构造函数

例子

无参数构造函数

Java 继承隐藏

Java面向对象的设计 - Java继承隐藏

方法隐藏

字段隐藏

例子

Java 抽象类和方法

Java面向对象设计 - Java抽象类和方法

语法

例子

Java 泛型类

Java面向对象设计 - Java泛型类

语法

形式参数

用法

例子

Java 泛型约束

Java面向对象设计 - Java泛型约束

无限通配符

上限通配符

下限通配符

Java 泛型方法和构造函数

Java面向对象设计 - Java泛型方法和构造函数

泛型方法

例子

使用泛型方法

例2

泛型构造函数

泛型对象创建中的类型推断


Java 内部类

Java面向对象的设计 - Java 内部类

什么是内部类?

作为包的成员的类被称为顶级类。

一个类可以在另一个类中声明。这种类型的类称为内部类。

如果在另一个类中声明的类被显式或隐式声明为static,它被称为嵌套类,而不是内部类。

包含内部类的类称为封闭类或外部类。

例子

下面的代码声明一个内部类。

class Outer {public class Inner {// Members of the Inner class go here}// Other members of the Outer class go here
}

Outer类是一个顶级类。

Inner类是一个内部类。它是外类的成员。

外层类是Inner类的封闭(外部)类。

内部类可以是另一个内部类的封闭类。内部类的嵌套层次没有限制。

内部类的实例只能存在于其封闭类的实例中。

使用内部类的优点

以下是内部类的一些优点。

  • 在将使用它们的其他类附近定义类。
  • 提供了一个额外的命名空间来管理类结构。
  • 一些设计模式使用内部类更容易实现。
  • 实现回调机制使用内部类是优雅和方便的。它有助于在Java中实现闭包。

访问局部变量的限制

下面的代码演示了访问局部内部类中的局部变量的规则。

main()方法声明两个局部变量x和y。这两个变量都是最终的。

变量x在被初始化之后从不改变,变量y不能被改变,因为它被声明为final。

public class Main {public static void main(String... args) {int x = 1;final int y = 2;class LocalInner {void print() {System.out.println("x = " + x);System.out.println("y = " + y);}}/** Uncomment the following statement will make the variable x no longer* an effectively final variable and the LocalIneer class will not compile.*/// x = 100;LocalInner li = new LocalInner();li.print();}
}

上面的代码生成以下结果。

内部类和继承

内部类可以继承另一个内部类,顶级类或其封闭类。

class A {public class B {}public class C extends B {}public class D extends A {}
}class E extends A {public class F extends B {}
}

内部类中没有静态成员

Java中的关键字static使一个构造成为一个顶层结构。

因此,我们不能为内部类声明任何静态成员(字段,方法或初始化器)。

允许在内部类中有作为编译时常量的静态字段。

class A {public class B {public final static int DAYS_IN_A_WEEK = 7; // OKpublic final String str = new String("Hello");}
}

生成的内部类的类文件

每个内部类都被编译成一个单独的类文件。

成员内部类和静态内部类的类文件名格式如下:

<outer-class-name>$<member-or-static-inner-class-name>

局部内部类的类文件名的格式如下:

<outer-class-name>$<a-number><local-inner-class-name>

匿名类的类文件名的格式如下:

<outer-class-name>$<a-number>

类文件名中的<a-number>是从1开始顺序生成的数字,以避免任何名称冲突。

静态上下文中的内类

我们可以在静态上下文中定义一个内部类,例如静态方法或静态初始化器。

所有静态字段成员都可以访问这样的内部类。

class Outer {static int k = 1;int m = 2;public static void staticMethod() {// Class Inner is defined in a static contextclass Inner {int j = k; // OK. Referencing static field k// int n = m; // An error. Referencing non-static field m}}
}

Java 内部类类型

Java面向对象设计 - Java内部类类型

您可以在类中的任何位置定义内部类,您可以在其中编写Java语句。

有三种类型的内部类。内部类的类型取决于位置和声明的方式。

  • 成员内部类
  • 局部内部类
  • 匿名内部类

成员内部类

成员内部类在类中声明的方式与声明成员字段或成员方法相同。

它可以声明为public,private,protected或package-level。

成员内部类的实例可以仅存在于其封闭类的实例内。

以下代码创建了一个成员内部类。

class Car {private int year;// A member inner class named Tire publicclass Tire {private double radius;public Tire(double radius) {this.radius = radius;}public double getRadius() {return radius;}} // Member inner class declaration ends here// A constructor for the Car classpublic Car(int year) {this.year = year;}public int getYear() {return year;}
}

局部内部类

一个局部内部类在块中声明。其范围仅限于声明它的块。

由于其范围限于其封闭块,因此其声明不能使用任何访问修饰符,例如public,private或protected。

通常,在方法内定义局部内部类。但是,它也可以在静态初始化器,非静态初始化器和构造器中定义。

下面的代码显示了一个局部内部类的例子。

import java.util.ArrayList;
import java.util.Iterator;public class Main {public static void main(String[] args) {StringList tl = new StringList();tl.addTitle("A");tl.addTitle("B");Iterator iterator = tl.titleIterator();while (iterator.hasNext()) {System.out.println(iterator.next());}}
}
class StringList {private ArrayList<String> titleList = new ArrayList<>();public void addTitle(String title) {titleList.add(title);}public void removeTitle(String title) {titleList.remove(title);}public Iterator<String> titleIterator() {// A local inner class - TitleIteratorclass TitleIterator implements Iterator<String> {int count = 0;@Overridepublic boolean hasNext() {return (count < titleList.size());}@Overridepublic String next() {return titleList.get(count++);}}TitleIterator titleIterator = new TitleIterator();return titleIterator;}
}

上面的代码生成以下结果。

例子

下面的代码有一个局部内部类继承自另一个公共类。

import java.util.Random;abstract class IntGenerator {public abstract int getValue() ;
}
class LocalGen {public IntGenerator getRandomInteger() {class RandomIntegerLocal extends IntGenerator {@Overridepublic int getValue() {Random rand = new Random();long n1 = rand.nextInt();long n2 = rand.nextInt();int value = (int) ((n1 + n2) / 2);return value;}}return new RandomIntegerLocal();} // End of getRandomInteger() method
}public class Main {public static void main(String[] args) {LocalGen local = new LocalGen();IntGenerator rLocal = local.getRandomInteger();System.out.println(rLocal.getValue());System.out.println(rLocal.getValue());}
}

上面的代码生成以下结果。

匿名内部类

匿名内部类没有名称。因为它没有名称,它不能有一个构造函数。

匿名类是一次性类。您定义一个匿名类并同时创建它的对象。

创建匿名类及其对象的一般语法如下:

new Interface()  {
// Anonymous  class body  goes  here
}

new Superclass(<argument-list-for-a-superclass-constructor>)  {
// Anonymous  class body  goes  here
}

new运算符用于创建匿名类的实例。

它后面是现有的接口名称或现有的类名称。

接口名称或类名称不是新创建的匿名类的名称。

如果使用接口名称,则匿名类实现接口。

如果使用类名,则匿名类继承自类。

仅当新运算符后面跟有类名时,才使用<argument-list>。如果新运算符后跟接口名称,则它为空。

如果<argument-list>存在,它包含要调用的现有类的构造函数的实际参数列表。

匿名类主体像往常一样在大括号中。

匿名类主体应该简短,以便更好的可读性。

下面的代码包含一个简单的匿名类,它在标准输出上打印一条消息。

public class Main {public static void main(String[] args) {new Object() {// An instance initializer{System.out.println("Hello from  an  anonymous class.");}}; // A semi-colon is necessary to end the statement}
}

上面的代码生成以下结果。

例2

以下代码使用匿名类来创建Iterator。

import java.util.ArrayList;
import java.util.Iterator;public class Main {private ArrayList<String> titleList = new ArrayList<>();public void addTitle(String title) {titleList.add(title);}public void removeTitle(String title) {titleList.remove(title);}public Iterator<String> titleIterator() {// An anonymous classIterator<String> iterator = new Iterator<String>() {int count = 0;@Overridepublic boolean hasNext() {return (count < titleList.size());}@Overridepublic String next() {return titleList.get(count++);}}; // Anonymous inner class ends herereturn iterator;}
}

Java 静态对象类

Java面向对象设计 - Java静态内部类

静态成员类不是内部类

在另一个类的主体中定义的成员类可以声明为静态。

例子

以下代码声明了顶级类A和静态成员类B:

class A {// Static member classpublic static class B {// Body for class B goes here}
}

注意

静态成员类不是内部类。它被认为是一个顶级类。

静态成员类也称为嵌套顶级类。

A类的实例和B类的实例可以独立存在,因为它们都是顶级类。

静态成员类可以声明为public,protected,package-level或private,以限制其在其封闭类之外的可访问性。

使用静态成员类有两个好处:

  • 静态成员类可以访问其包含类的静态成员,包括私有静态成员。
  • 一个包通过提供一个命名空间,就像一个顶级类的容器。具有静态成员类的顶级类提​​供了额外的命名空间层。

静态成员类是其封闭顶级类的直接成员,而不是包的成员。

静态成员类的对象的创建方式与使用new运算符创建顶级类的对象的方式相同。要创建一个B类的对象,你写

A.B bReference = new A.B();

由于类B的简单名称在类A中的范围内,因此我们可以使用其简单名称在类A中创建其对象

B  bReference2 = new B(); // This  statement appears inside class  A  code

我们还可以通过导入com.java2s.innerclasses.A.B类,使用A类之外的简单名称B.

例2

下面的代码显示了如何使用静态内部类。

public class Main {public static void main(String[] args) {Car.Tire m = new Car.Tire(17);Car.Tire m2 = new Car.Tire(19);Car.Keyboard k = new Car.Keyboard(122);Car.Keyboard k1 = new Car.Keyboard(142);System.out.println(m);System.out.println(m2);System.out.println(k);System.out.println(k1);}
}
class Car {// Static member class - Monitorpublic static class Tire {private int size;public Tire(int size) {this.size = size;}public String toString() {return "Monitor   - Size:" + this.size + "  inch";}}// Static member class - Keyboardpublic static class Keyboard {private int keys;public Keyboard(int keys) {this.keys = keys;}public String toString() {return "Keyboard  - Keys:" + this.keys;}}
}

上面的代码生成以下结果。

Java 内部类对象

Java面向对象设计 - Java内部类对象

局部内部类的对象是使用块中的新运算符创建的,它声明了类。

在声明类的同时创建一个匿名类的对象。

静态成员类是另一种类型的顶级类。

您可以按照创建顶级类的对象的方式创建静态成员类的对象。

成员内部类的实例始终存在于其封闭类的实例中。

语法

创建成员内部类的实例的一般语法如下:

OuterClassReference.new MemberInnerClassConstructor()

OuterClassReference是包围类的引用,后跟一个后跟新运算符的点。

例子

成员内部类的构造函数调用遵循new运算符。

class Outer  {public class  Inner {}
}

要创建内部成员内部类的实例,必须首先创建其封闭类Outer的实例。

Outer  out  = new Outer();

现在,您需要在out参考变量上使用new运算符来创建Inner类的对象。

out.new Inner();

为了将内部成员内部类的实例的引用存储在引用变量中,我们可以写下面的语句:

Outer.Inner in = out.new   Inner();

以下代码显示了如何创建成员内部类的对象

public class Main {public static void main(String[] args) {Car c = new Car();Car.Tire t = c.new Tire(9);}
}
class Car {public class Tire {private int size;public Tire(int size) {this.size = size;}public String toString() {return "Monitor   - Size:" + this.size + "  inch";}}}

Java 内部类成员

Java面向对象设计 - Java内部类成员

内部类可以访问其所有实例成员,实例字段和其封闭类的实例方法。

class Outer {private int value = 2014;public class Inner {public void printValue() {System.out.println("Inner: Value  = " + value);}} // Inner class ends herepublic void printValue() {System.out.println("Outer: Value  = " + value);}public void setValue(int newValue) {this.value = newValue;}
}public class Main {public static void main(String[] args) {Outer out = new Outer();Outer.Inner in = out.new Inner();out.printValue();in.printValue();out.setValue(2015);out.printValue();in.printValue();}
}

上面的代码生成以下结果。

例子

以下代码显示如何访问内部类的内部变量。

public class Main {public static void main(String[] args) {Outer out = new Outer();Outer.Inner in = out.new Inner();out.printValue();in.printValue();out.setValue(3);out.printValue();in.printValue();}
}
class Outer {private int value = 1;public class Inner {private int value = 2;public void printValue() {System.out.println("Inner: Value  = " + value);}} // Inner class ends herepublic void printValue() {System.out.println("Outer: Value  = " + value);}public void setValue(int newValue) {this.value = newValue;}
}

上面的代码生成以下结果。

在内部类中使用关键字this

以下代码显示如何在内部类中使用关键字this。

class Outer {private int value = 1;class QualifiedThis {private int value = 2;public void printValue() {System.out.println("value=" + value);System.out.println("this.value=" + this.value);System.out.println("QualifiedThis.this.value=" + QualifiedThis.this.value);}public void printHiddenValue() {int value = 2;System.out.println("value=" + value);System.out.println("this.value=" + this.value);System.out.println("QualifiedThis.this.value=" + QualifiedThis.this.value);}}public void printValue() {System.out.println("value=" + value);System.out.println("this.value=" + this.value);}  
}public class Main {public static void main(String[] args) {Outer outer = new Outer();Outer.QualifiedThis qt = outer.new QualifiedThis();System.out.println("printValue():");qt.printValue();System.out.println("printHiddenValue():");qt.printHiddenValue();outer.printValue();}
}

上面的代码生成以下结果。

隐藏变量

如果实例变量名称被隐藏,您必须使用关键字this或类名称以及关键字this限定其名称。

class TopLevelOuter {private int v1 = 100;// Here, only v1 is in scopepublic class InnerLevelOne {private int v2 = 200;// Here, only v1 and v2 are in scopepublic class InnerLevelTwo {private int v3 = 300;// Here, only v1, v2, and v3 are in scopepublic class InnerLevelThree {private int v4 = 400;// Here, all v1, v2, v3, and v4 are in scope}}}
}

从外部类引用变量

以下代码显示如何从外部类引用变量。

public class Test{private int value = 1;public class Inner {private int value = 2;public void printValue() {System.out.println("Inner: Value  = " + value);System.out.println("Outer: Value  = " + Test.this.value);}} // Inner class ends herepublic void printValue() {System.out.println("\nOuter - printValue()...");System.out.println("Outer: Value  = " + value);}public void setValue(int newValue) {System.out.println("\nSetting  Outer"s value to " + newValue);this.value = newValue;}}

Java 继承

Java面向对象设计 - Java继承

子类可以从超类继承。超类也称为基类或父类。子类也称为派生类或子类。

从另一个类继承一个类非常简单。我们在子类的类声明中使用关键字extends,后跟超类名称。

Java不支持多重继承的实现。

Java中的类不能有多个超类。

语法

一般的语法是

<class modifiers>class <SubclassName> extends <SuperclassName> {// Code for the   Subclass
}

例子

以下代码显示如何使用从Employee类创建Manager类。

class Employee {private String name = "Unknown";public void setName(String name) {this.name = name;}public String getName() {return name;}
}
class Manager extends Employee {
}
public class Main {public static void main(String[] args) {// Create an object of the Manager classManager mgr = new Manager();// Set the name of the managermgr.setName("Tom");// Get the name of the managerString mgrName = mgr.getName();// Display the manager nameSystem.out.println("Manager Name: " + mgrName);}
}

上面的代码生成以下结果。

注意

我们没有为Manager类编写任何代码,它的工作原理与Employee类相同,因为它继承自Employee类。

您可以使用Manager类的构造函数创建一个管理器对象。

Manager mgr = new Manager();

创建管理器对象后,Manager对象的工作方式与Employee对象相同。

我们对manager对象使用了setName()和getName()方法。

mgr.setName("Tom"); 
String mgrName  = mgr.getName();

Manager类不声明setName()和getName()方法。Manager类“扩展Employee"。

当一个类从另一个类继承时,它继承它的超类成员,实例变量,方法等。

对象父类

对象类是默认超类。

所有类都隐式继承自Object类。因此所有类的对象都可以使用Object类的方法。

public class P {}

类P从Object扩展,即使我们没有指定父类。

Object类声明了hashCode()和toString()方法。因为Employee类是Object类的一个子类,它可以使用这些方法。

Employee emp = new Employee();
int hc  = emp.hashCode(); 
String str = emp.toString();

Employee类不使用extends子句指定其超类。这意味着它继承自Object类。

Object类声明了hashCode()和toString()方法。因为Employee类是Object类的一个子类,它可以使用这些方法。

向上转换和向下转换

现实世界中的“is-a”关系在软件中转化为继承类层次结构。

例如,Manager是特定类型的Employee。 Employee是一种特定类型的Object。

当您在继承层次结构中向上移动时,将从特定类型移动到更一般的类型。

从子类到超类的赋值称为上转换,它在Java中始终允许。

class Employee {private String name = "Unknown";public void setName(String name) {this.name = name;}public String getName() {return name;}
}class Manager extends Employee {
}public class Main {public static void printName(Employee emp) {String name = emp.getName();System.out.println(name);}public static void main(String[] args) {Employee emp = new Employee();emp.setName("Tom");Manager mgr = new Manager();mgr.setName("Jack"); // Inheritance of setName() at work// Print namesprintName(emp);printName(mgr); // Upcasting at work}
}

为子类变量分配超类引用称为向下转换。

向下转换与向上转换相反。

在向上转换中,赋值向上移动类层次结构,而在向下转换中,赋值向下移动类层次结构。

我们必须在向下转换中使用类型转换。

Manager mgr = (Manager)emp;  // OK. Downcast  at work

上面的代码生成以下结果。

instanceof运算符

Java instanceof运算符帮助我们确定引用变量是否有对类或子类的引用。

它需要两个操作数,并计算为布尔值true或false。

它的语法是

<Class Reference Variable> instanceof <Class Name or Interface>

如果<Class Reference Variable>引用类<Class Name>或其任何后代的对象,instanceof返回true。

如果引用变量为null,instanceof总是返回false。

我们应该在向下转换之前使用instanceof运算符。

Manager mgr = new Manager(); 
Employee emp = mgr;
if (emp instanceof Manager)  {// downcast  will succeed mgr = (Manager)emp;
}else  {// emp is not  a  Manager type
}

禁用继承

我们可以通过声明类final来禁用子类。

最终的类不能被子类化。

下面的代码声明了一个名为MyClass的最终类:

public final  class  MyClass{}

我们也可以声明一个方法为final。最终方法不能被子类覆盖或隐藏。

public class A  {public final  void  m1()  {}public void  m2()  {}
}

Java 方法重写

Java面向对象设计 - Java方法重写

方法重写

重新定义从超类继承的类中的实例方法称为方法重写。

例子

让我们考虑类A和类B的以下声明:

public class A  {public void  print() { System.out.println("A");}
}public class B  extends A  {public void  print() { System.out.println("B");}
}

类B是类A的子类。类B从其超类继承print()方法并重新定义它。

类B中的print()方法覆盖类A的print()方法。

如果一个类覆盖了一个方法,它会影响覆盖的类及其子类。考虑下面的类C的声明:

public class C  extends B  {// Inherits B.print()
}

类C不声明任何方法。它继承类B中的print()方法。

注意

类总是继承它的直接超类的可用的。

方法必须是实例方法。重写不适用于静态方法。

重写方法必须具有与重写方法相同的名称。

重写方法必须具有与重写方法相同顺序的相同类型的相同数量的参数。

当方法的参数使用通用类型时,考虑通用类型参数的擦除,而不是通用类型本身与其他方法比较。

参数的名称无关紧要。

如果重写方法的返回类型是引用类型,则重写方法的返回类型必须与重写方法的返回类型兼容。

访问级别

重写方法的访问级别必须至少与重写方法的访问级别相同或更宽松。

下表列出了重写方法允许的访问级别

重写方法访问级别允许重写方法访问级别...
publicpublic
protectedpublic, protected
package-levelpublic, protected, package-level

方法可以在其throws子句中包括检查异常的列表。重写方法无法向重写方法中的异常列表添加新的异常。

它可以删除一个或所有异常,或者可以用另一个异常替换异常。

访问重写方法

从子类访问重写的方法。子类可以使用关键字 super 作为限定符来调用超类的重写方法。

class MySuperClass {public void print() {System.out.println("Inside MySuperClass");}
}class MySubClass extends MySuperClass {public void print() {// Call print() method of MySuperClass classsuper.print();// Print a messageSystem.out.println("Inside MySubClass.print()");}public void callOverridenPrint() {// Call print() method of MySuperClass class super.print();}
}public class Main {public static void main(String[] args) {MySubClass aoSub = new MySubClass();aoSub.print();aoSub.callOverridenPrint();}
}

上面的代码生成以下结果。

Java 继承和构造函数

Java面向对象设计 - Java继承和构造函数

构造函数不是类的成员,它们不是由子类继承的。

它们用于初始化实例变量。

class CSuper {public CSuper() {System.out.println("Inside CSuper() constructor.");}
}class CSub extends CSuper {public CSub() {System.out.println("Inside CSub()  constructor.");}
}public class Main {public static void main(String[] args) {CSub cs = new CSub();}
}

上面的代码生成以下结果。

例子

下面显示了如何编译器注入一个super()来立即调用父类的无参数构造函数。

class CSuper {public CSuper() {super(); // Injected by the compilerSystem.out.println("Inside CSuper() constructor.");}
}class CSub extends CSuper {public CSub() {super(); // Injected by the compilerSystem.out.println("Inside CSub()  constructor.");}
}public class Main {public static void main(String[] args) {CSub cs = new CSub();}
}

上面的代码生成以下结果。

关键字super指的是类的直接父类。

我们可以调用超类构造函数,只使用super关键字作为构造函数中的第一个语句。

无参数构造函数

我们可以将超类的no-args构造函数或任何其他构造函数显式地调用为类的构造函数中的第一个语句。

只有在没有明确添加的情况下,编译器才会注入no-args构造函数调用。

class Employee {private String name = "Unknown";public Employee(String name) {this.name = name;}public void setName(String name) {this.name = name;}public String getName() {return name;}
}class Manager extends Employee {public Manager(String name) {super(name);}
}public class Main {public static void main(String[] args) {Manager mgr = new Manager("Tom");String name = mgr.getName();System.out.println("Manager name:  " + name);}
}

上面的代码生成以下结果。

每个类都必须直接或间接地从其构造函数中调用其超类的构造函数。

如果超类没有no-args构造函数,我们必须显式地调用超类的任何其他构造函数。

Java 继承隐藏

Java面向对象的设计 - Java继承隐藏

方法隐藏

类从其超类继承所有非私有静态方法。

重定义类中继承的静态方法称为方法隐藏。

子类中的重定义静态方法隐藏其超类的静态方法。

在类中重定义非静态方法称为方法覆盖。

关于方法隐藏的重定义方法(名称,访问级别,返回类型和异常)的所有规则与方法覆盖相同。

class MySuper {public static void print() {System.out.println("Inside MySuper.print()");}
}class MySubclass extends MySuper {public static void print() {System.out.println("Inside MySubclass.print()");}
}public class Main {public static void main(String[] args) {MySuper mhSuper = new MySub();MySubclass mhSub = new MySubclass();MySuper.print();MySubclass.print();((MySuper) mhSub).print();mhSuper = mhSub;mhSuper.print();((MySubclass) mhSuper).print();}
}

上面的代码生成以下结果。

字段隐藏

类中的字段声明(静态或非静态)在其父类中隐藏具有相同名称的继承字段。

在字段隐藏的情况下,不考虑字段的类型及其访问级别。

字段隐藏仅基于字段名称。

class MySuper {protected int num = 100;protected String name = "Tom";
}class MySub extends MySuper {public void print() {System.out.println("num: " + num);System.out.println("name: " + name);}
}class MySub2 extends MySuper {// Hides num field in MySuper classprivate int num = 200;// Hides name field in MySuper classprivate String name = "Jack";public void print() {System.out.println("num: " + num);System.out.println("name: " + name);}
}public class Main {public static void main(String[] args) {MySub fhSub = new MySub();fhSub.print();MySub2 fhSub2 = new MySub2();fhSub2.print();}
}

上面的代码生成以下结果。

例子

以下代码显示了如何使用super关键字访问超类的隐藏字段

class MySuper {protected int num = 100;protected String name = "Tom";
}class MySub extends MySuper {// Hides the num field in MySuper classprivate int num = 200;// Hides the name field in MySuper classprivate String name = "Jack";public void print() {System.out.println("num: " + num);System.out.println("super.num: " + super.num);System.out.println("name: " + name);System.out.println("super.name: " + super.name);}
}public class Main {public static void main(String[] args) {MySub s = new MySub();s.print();}
}

上面的代码生成以下结果。

字段隐藏发生在一个类声明一个变量与来自其超类的继承变量具有相同名称的时候。

字段隐藏仅基于字段的名称。

类应该使用关键字super来访问超类的隐藏字段。

类可以使用简单的名称来访问其主体中的重定义字段

Java 抽象类和方法

Java面向对象设计 - Java抽象类和方法

Java可以定义一个类,其对象不能被创建。

它的目的只是表示一个想法,这是其他类的对象共有的。

这样的类称为抽象类。

语法

我们需要在类声明中使用 abstract 关键字来声明一个抽象类。

例如,下面的代码声明一个Shape类的抽象:

public abstract class Shape  {}

下面的代码为Shape类添加了一个draw()方法。

public abstract class Shape  {public  Shape() {}public abstract  void  draw();
}

抽象类不一定意味着它具有至少一个抽象方法。

如果一个类有一个被声明或继承的抽象方法,它必须被声明为抽象。

抽象方法的声明方式与任何其他方法相同,只是它的主体由分号表示。

例子

下面的Shape类有抽象和非抽象方法。

abstract class Shape {private String name;public Shape() {this.name = "Unknown  shape";}public Shape(String name) {this.name = name;}public String getName() {return this.name;}public void setName(String name) {this.name = name;}// Abstract methodspublic abstract void draw();public abstract double getArea();public abstract double getPerimeter();
}

下面的代码展示了如何创建一个Rectangle类,它继承自Shape类。

class Rectangle extends Shape {private double width;private double height;public Rectangle(double width, double height) {// Set the shape name as "Rectangle"super("Rectangle");this.width = width;this.height = height;}// Provide an implementation for inherited abstract draw() methodpublic void draw() {System.out.println("Drawing a  rectangle...");}// Provide an implementation for inherited abstract getArea() methodpublic double getArea() {return width * height;}// Provide an implementation for inherited abstract getPerimeter() methodpublic double getPerimeter() {return 2.0 * (width + height);}
}

Java 泛型类

Java面向对象设计 - Java泛型类

使用泛型,我们可以在不知道代码操作对象的类型的情况下编写代码。它允许我们创建泛型类,构造函数和方法。

泛型类是使用形式类型参数定义的。

形式类型参数是一个逗号分隔的变量名列表,位于类声明中类名后面的尖括号<>中。

语法

下面的代码声明一个类Wrapper,它接受一个形式类型参数:

public class Wrapper<T>  {}

该参数已命名为T.

T是一个类型变量,它可以是Java中的任何引用类型,例如String,Integer,Double等。

当使用Wrapper类时,指定形式类型参数值。

形式参数

采用形式类型参数的类也称为参数化类。

您可以声明Wrapper类的变量,将String类型指定为其形式类型参数的值,如下所示。

这里,String是实际的类型参数。

Wrapper<String> stringWrapper;

Java允许我们使用泛型类而不指定形式类型参数。

用法

这是为了向后兼容。您还可以声明Wrapper<T>类的变量,如下所示:

Wrapper  aRawWrapper;

当使用泛型类而不指定实际的类型参数时,它被称为原始类型。上面的声明使用Wrapper <T>类作为原始类型,因为它没有指定T的值。

泛型类的实际类型参数必须是引用类型。

原始类型不允许作为泛型类的实际类型参数。

类可以采用多个正式类型参数。下面的代码声明一个Mapper类,它接受两个形式参数T和R:

public class Mapper<T, R>    {}

我们可以声明Mapper <T,R>类的变量如下:

Mapper<String, Integer>  mapper;

这里,实际的类型参数是String和Integer。

形式类型参数在类体中可用作类型。

public class Wrapper<T> {private T obj;public Wrapper(T obj) {this.obj = obj;}public T get() {return obj;}public void set(T obj) {this.obj = obj;}
}

Wrapper<T>类使用形式类型参数来声明实例变量obj,以声明其构造函数和set()方法的形式参数,以及作为get()方法的返回类型。

您可以通过为构造函数指定实际的类型参数来创建泛型类型的对象,如下所示:

Wrapper<String> w1  = new Wrapper<String>("Hello");

我们可以省略实际的类型参数。在下面的代码中,编译器会将构造函数的实际类型参数推断为String:

Wrapper<String> w1  = new Wrapper<>("Hello");

一旦你声明了泛型类的一个变量,你可以把形式类型参数看作是指定的实际类型参数。

现在,你可以认为,对于w1,Wrapper类的get()方法返回一个String。

String s1=  w1.get();

例子

以下代码显示如何使用Wrapper类。

public class Main {public static void main(String[] args) {Wrapper<String> w1 = new Wrapper<>("Hello");String s1 = w1.get();System.out.println("s1=" + s1);w1.set("Testing generics");String s2 = w1.get();System.out.println("s2=" + s2);w1.set(null);String s3 = w1.get();System.out.println("s3=" + s3);}
}class Wrapper<T> {private T obj;public Wrapper(T obj) {this.obj = obj;}public T get() {return obj;}public void set(T obj) {this.obj = obj;}
}

Java 泛型约束

Java面向对象设计 - Java泛型约束

无限通配符

通配符类型由问号表示,如<?> 。

对于通用类型,通配符类型是对象类型用于原始类型。

我们可以将任何已知类型的泛型分配为通配符类型。

这里是示例代码:

// MyBag  of  String type
MyBag<String> stringMyBag  = new MyBag<String>("Hi");// You can  assign a  MyBag<String> to  MyBag<?> type
MyBag<?> wildCardMyBag  = stringMyBag;

通配符通配类型中的问号(例如,<?>)表示未知类型。

当您使用通配符声明参数化类型作为参数类型时,这意味着它不知道它的类型。

MyBag<?> unknownMyBag = new MyBag<String>("Hello");

上限通配符

我们表示通配符的上限

<? extends T>

这里,T是一种类型。<? extends T>表示任何类型为T或其子类是可接受的。

例如,上限可以是数字类型。

如果我们通过任何其他类型,该类型是数字类型的子类,很好。但是,不是Number类型或其子类型的任何东西都应该在编译时被拒绝。

使用上限作为数字,我们可以将方法定义为

class MyBag<T> {private T ref;public MyBag(T ref) {this.ref = ref;}public T get() {return ref;}public void set(T a) {this.ref = a;}
}public class Main {public static double sum(MyBag<? extends Number> n1,MyBag<? extends Number> n2) {Number num1 = n1.get();Number num2 = n2.get();double sum = num1.doubleValue() + num2.doubleValue();return sum;}}

不管你为n1和n2传递什么,它们将始终与Number的赋值兼容,因为编译器确保传递给sum()方法的参数遵循其声明中指定的规则 <? extends Number>。

下限通配符

指定下限通配符与指定上限通配符相反。

使用下限通配符的语法是<? super T>,这意味着“任何是T的超类型”。

class MyBag<T> {private T ref;public MyBag(T ref) {this.ref = ref;}public T get() {return ref;}public void set(T a) {this.ref = a;}
}
public class Main {public static <T> void copy(MyBag<T> source, MyBag<? super T> dest) {T value = source.get();dest.set(value);}
}

Java 泛型方法和构造函数

Java面向对象设计 - Java泛型方法和构造函数

泛型方法

我们可以在方法声明中定义类型参数,它们在方法的返回类型之前的尖括号中指定。

包含泛型方法声明的类型不必是泛型类型。

我们可以在非静态方法声明中使用为泛型类型指定的类型参数。

例子

以下代码显示如何为方法m1()定义新的类型参数V.

新类型参数V强制方法m1()的第一个和第二个参数为相同类型。

第三个参数必须是相同的类型T,这是类实例化的类型。

class MyBag<T> {private T ref;public MyBag(T ref) {this.ref = ref;}public T get() {return ref;}public void set(T a) {this.ref = a;}
}
class Test<T>  {public <V>  void  m1(MyBag<V>  a, MyBag<V>  b, T  c)  {}
}

使用泛型方法

要传递方法的形式类型参数的实际类型参数,我们必须在方法调用中的点和方法名之间的尖括号<>中指定它。

class MyBag<T> {private T ref;public MyBag(T ref) {this.ref = ref;}public T get() {return ref;}public void set(T a) {this.ref = a;}
}
class Test<T> {public <V> void m1(MyBag<V> a, MyBag<V> b, T c) {}
}
public class Main {public static void main(String[] argv) {Test<String> t = new Test<String>();MyBag<Integer> iw1 = new MyBag<Integer>(new Integer(201));MyBag<Integer> iw2 = new MyBag<Integer>(new Integer(202));// Specify that Integer is the actual type for the type parameter for m1()t.<Integer>m1(iw1, iw2, "hello");t.m1(iw1, iw2, "hello");}
}

例2

以下代码显示了如何声明泛型静态方法。

我们不能在静态方法内引用包含类的类型参数。

静态方法只能引用它自己声明的类型参数。

以下静态通用类型定义了类型参数T,用于约束参数source和dest的类型。

class MyBag<T> {private T ref;public MyBag(T ref) {this.ref = ref;}public T get() {return ref;}public void set(T a) {this.ref = a;}
}public class Main {public static <T> void copy(MyBag<T> source, MyBag<? super T> dest) {T value = source.get();dest.set(value);}public static void main(String[] argv) {}
}

要为静态方法调用指定实际的类型参数,我们可以这样做:

Main.<Integer>copy(iw1, iw2);

泛型构造函数

我们可以为构造函数定义类型参数。

下面的代码定义了类Test的构造函数的类型参数U.

它放置一个约束,即构造函数的类型参数U必须是相同的,或者它的类类型参数T的实际类型的子类型。

public class Test<T> {public <U extends T> Test(U k) {}
}

要为构造函数指定实际的类型参数值,请在新运算符和构造函数名称之间的尖括号中指定它,如以下代码段所示:

class Test<T> {public <U extends T> Test(U k) {}
}public class Main {public static void main(String[] argv) {// Specify the actual type parameter for the constructor as DoubleTest<Number> t1 = new<Double> Test<Number>(new Double(1.9));// Let the compiler figure out, Integer is// the actual type parameter for the constructorTest<Number> t2 = new Test<Number>(new Integer(1));}
}

泛型对象创建中的类型推断

Java 7在泛型类型的对象创建表达式中增加了对类型推断的有限支持。

对于以下语句:

List<String> list = new ArrayList<String>();

在Java 7中,可以在上面的语句中指定空尖括号,称为菱形操作符或简单的菱形<>作为ArrayList的类型参数。

List<String> list = new ArrayList<>(); 

如果我们不在对象创建表达式中为泛型类型指定类型参数,那么类型是原始类型,编译器生成未检查的警告。

例如,以下语句将编译未选中的警告:

List<String> list = new ArrayList(); // Generates an  unchecked  warning

我们不能创建泛型的异常类。并且没有泛型的匿名类。

相关文章:

Java 新手教程!面向对象设计一口气讲完![]~( ̄▽ ̄)~*(中)

目录 Java 内部类 Java面向对象的设计 - Java 内部类 什么是内部类&#xff1f; 例子 使用内部类的优点 访问局部变量的限制 内部类和继承 内部类中没有静态成员 生成的内部类的类文件 静态上下文中的内类 Java 内部类类型 Java面向对象设计 - Java内部类类型 成员内…...

驰骋低代码功能升级 - 实体功能权限控制

1. 权限控制升级概述 新增功能&#xff1a;对新建、保存、删除、归档、撤销归档等操作的按钮进行精细化的权限控制。展示位置&#xff1a;这些权限控制体现在查询页面和实体卡片页面的工具栏按钮上。 2. 权限控制方式 新建 0. 不控制&#xff1a;任何人都可以新建。1. 指定岗…...

Matlab|考虑阶梯式碳交易机制与电制氢的综合能源系统热电优化

目录 1 主要内容 2 部分程序 3 程序结果 4 下载链接 1 主要内容 该程序复现《考虑阶梯式碳交易机制与电制氢的综合能源系统热电优化》&#xff0c;主要内容&#xff1a;“双碳”背景下&#xff0c;为提高能源利用率&#xff0c;优化设备的运行灵活性&#xff0c;进一步降低…...

Midjourney零基础学习

Midjourney学习笔记TOP01 什么是AI艺术 AI艺术指的是使用AI技术创作的艺术作品&#xff0c;包括AI诗歌、AI音乐、AI绘画等多种艺术表现形式&#xff1b;AI艺术可以被视为计算机程序与人类合作创作作品&#xff1b;除了Midjourney&#xff0c;比较流行的AI图像生成工具还有Stab…...

词嵌入(Word Embedding)之Word2Vec、GloVe、FastText

简介&#xff1a;个人学习分享&#xff0c;如有错误&#xff0c;欢迎批评指正。 词嵌入&#xff08;Word Embedding&#xff09;是一种将词语映射到低维稠密向量空间的技术&#xff0c;能够捕捉词与词之间的语义关系。Word2Vec、GloVe、FastText 是常见的词嵌入方法&#xff0c…...

Vue82 路由器的两种工作模式 以及 node express 部署前端

笔记 对于一个url来说&#xff0c;什么是hash值&#xff1f;—— #及其后面的内容就是hash值。hash值不会包含在 HTTP 请求中&#xff0c;即&#xff1a;hash值不会带给服务器。hash模式&#xff1a; 地址中永远带着#号&#xff0c;不美观 。若以后将地址通过第三方手机app分享…...

[C#]使用纯opencvsharp部署yolov11-onnx图像分类模型

【官方框架地址】 https://github.com/ultralytics/ultralytics.git 【算法介绍】 使用纯OpenCvSharp部署YOLOv11-ONNX图像分类模型是一项复杂的任务&#xff0c;但可以通过以下步骤实现&#xff1a; 准备环境&#xff1a;首先&#xff0c;确保开发环境已安装OpenCvSharp和必…...

【机器学习-无监督学习】概率图模型

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈Python机器学习 ⌋ ⌋ ⌋ 机器学习是一门人工智能的分支学科&#xff0c;通过算法和模型让计算机从数据中学习&#xff0c;进行模型训练和优化&#xff0c;做出预测、分类和决策支持。Python成为机器学习的首选语言&#xff0c;…...

每日学习一个数据结构-AVL树

文章目录 概述一、定义与特性二、平衡因子三、基本操作四、旋转操作五、应用场景 Java代码实现 概述 AVL树是一种自平衡的二叉查找树&#xff0c;由两位俄罗斯数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明。想了解树的相关概念&#xff0c;请点击这里。以下是对AVL树的…...

课堂点名系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;论坛信息管理&#xff0c;基础数据管理&#xff0c;课程信息管理&#xff0c;课程考勤管理&#xff0c;轮播图信息 微信端账号功能包括&#xff1a;系统首页&#xff0c;论坛信…...

使用Python查找WeChat和QQ的安装路径和文档路径

在日常工作和生活中&#xff0c;我们经常需要查找某些应用程序的安装位置或者它们存储文件的位置。特别是对于像WeChat&#xff08;微信&#xff09;和QQ这样的即时通讯软件&#xff0c;了解它们的文件存储位置可以帮助我们更好地管理我们的聊天记录和共享文件。今天&#xff0…...

【AI大模型】深入Transformer架构:编码器部分的实现与解析(下)

目录 &#x1f354; 编码器介绍 &#x1f354; 前馈全连接层 2.1 前馈全连接层 2.2 前馈全连接层的代码分析 2.3 前馈全连接层总结 &#x1f354; 规范化层 3.1 规范化层的作用 3.2 规范化层的代码实现 3.3 规范化层总结 &#x1f354; 子层连接结构 4.1 子层连接结…...

【数据结构】【栈】算法汇总

一、顺序栈的操作 1.准备工作 #define STACK_INIT_SIZE 100 #define STACKINCREMENT 10 typedef struct{SElemType*base;SElemType*top;int stacksize; }SqStack; 2.栈的初始化 Status InitStack(SqStack &S){S.base(SElemType*)malloc(MAXSIZE*sizeof(SElemType));if(…...

如何训练自己的大模型,答案就在这里。

训练自己的AI大模型是一个复杂且资源密集型的任务&#xff0c;涉及多个详细步骤、数据集需求以及计算资源要求。以下是根据搜索结果提供的概述&#xff1a; 详细步骤 \1. 设定目标&#xff1a; - 首先需要明确模型的应用场景和目标&#xff0c;比如是进行分类、回归、生成文本…...

React18新特性

React 18新特性详解如下&#xff1a; 并发渲染&#xff08;Concurrent Rendering&#xff09;&#xff1a; React 18引入了并发渲染特性&#xff0c;允许React在等待异步操作&#xff08;如数据获取&#xff09;时暂停和恢复渲染&#xff0c;从而提供更平滑的用户体验。 通过时…...

汽车发动机系统EMS详细解析

汽车发动机系统EMS&#xff0c;全称Engine-Management-System&#xff08;发动机管理系统&#xff09;&#xff0c;是现代汽车电子控制技术的重要组成部分。以下是对汽车发动机系统EMS的详细解析&#xff0c;涵盖其定义、工作原理、主要组成、功能特点、技术发展以及市场应用等…...

【社保通-注册安全分析报告-滑动验证加载不正常导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…...

初学Vue(2)

文章目录 监视属性 watch深度监视computed 和 watch 之间的区别 绑定样式&#xff08;class style&#xff09;条件渲染列表渲染基本列表key的原理列表过滤列表排序收集表单中的数据 v-model过滤器&#xff08;Vue3已移除&#xff09; 监视属性 watch 当被监视的属性变化时&am…...

ThinkPHP5基础入门

文章目录 ThinkPHP5基础入门一、引言二、环境搭建1、前期准备2、目录结构 三、快速上手1、创建模块2、编写控制器3、编写视图4、编写模型 四、调试与部署1、调试模式2、关闭调试模式3、隐藏入口文件 五、总结 ThinkPHP5基础入门 一、引言 ThinkPHP5 是一个基于 MVC 和面向对象…...

Metal 之旅之MTLLibrary

什么是MSL? MSL是Metal Shading Language 的简称&#xff0c;为了更好的在GPU执行程序&#xff0c;苹果公司定义了一套类C的语言&#xff08;Metal Shading Language &#xff09;&#xff0c;在GPU运行的程序都是用这个语言来编写的。 什么是MTLLibrary? .metal后缀的文件…...

第十二章 Redis短信登录实战(基于Session)

目录 一、User类 二、ThreadLocal类 三、用户业务逻辑接口 四、用户业务逻辑接口实现类 五、用户控制层 六、用户登录拦截器 七、拦截器配置类 八、隐藏敏感信息的代码调整 完整的项目资源共享地址&#xff0c;当中包含了代码、资源文件以及Nginx&#xff08;Wi…...

华为OD机试 - 九宫格游戏(Python/JS/C/C++ 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…...

Pytorch库中torch.normal()详解

torch.normal()用法 torch.normal()函数&#xff0c;用于生成符合正态分布&#xff08;高斯分布&#xff09;的随机数。在 PyTorch 中&#xff0c;这个函数通常用于生成 Tensor。 该函数共有四个方法&#xff1a; overload def normal(mean: Tensor, std: Tensor, *, generat…...

atcoder-374(a-e)

atcoder-374 文章目录 atcoder-374ABC简洁的写法正解 D正解 E A #include<bits/stdc.h>using namespace std;signed main() {string s;cin>>s;string strs.substr(s.size()-3);if(str "san") puts("Yes");else puts("No");return 0…...

idea2024设置中文

今天下载idea2024.2版本&#xff0c;发现已经装过中文插件&#xff0c;但是还是不显示中文&#xff0c;找了半天原来还需要设置中文选项 方案一 点击文件 -> 关闭项目 点击自定义 -> 选择语言 方案二 点击文件 -> 设置 外观与行为 -> 系统设置 -> 语言和地区…...

跨境电商独立站轮询收款问题

想必做跨境电商独立站的小伙伴&#xff0c;对于PayPal是再熟悉不过了&#xff0c;PayPal是一个跨国际贸易的支付平台&#xff0c;对于做独立站的朋友来说跨境收款绝大部分都是依赖PayPal以及Stripe条纹了。简单来说PayPal跟国内的支付宝有点类似&#xff0c;但是PayPal它是跨国…...

[OS] 3.Insert and Remove Kernel Module

Insert and Remove Kernel Module 1. 切换到 root 账户 $ sudo su作用&#xff1a;Linux 内核模块的加载和卸载需要超级用户权限&#xff0c;因此你必须以 root 用户身份进行操作。sudo su 命令允许你从普通用户切换到 root 账户&#xff0c;从而获得系统的最高权限&#xff…...

updatedb命令:更新locate数据库

一、命令简介 ​updatedb ​命令用于更新 locate ​命令使用的文件数据库&#xff0c;以便 locate ​命令能够快速定位文件。 ‍ 二、命令参数 命令格式 updatedb [选项]选项 ​-l​: 仅更新本地文件系统&#xff08;默认行为&#xff09;​-U​: 更新所有文件系统​-o D…...

分布式共识算法ZAB

文章目录 概述一、ZAB算法概述二、ZAB算法的核心特性三、ZAB算法的工作流程四、ZAB算法的优势与局限 其他共识算法 概述 分布式共识算法ZAB&#xff0c;全称Zookeeper Atomic Broadcast&#xff08;Zookeeper原子广播&#xff09;&#xff0c;是Zookeeper保证数据一致性的核心…...

程序化交易与非程序化交易者盈利能力孰优孰劣

炒股自动化&#xff1a;申请官方API接口&#xff0c;散户也可以 python炒股自动化&#xff08;0&#xff09;&#xff0c;申请券商API接口 python炒股自动化&#xff08;1&#xff09;&#xff0c;量化交易接口区别 Python炒股自动化&#xff08;2&#xff09;&#xff1a;获取…...