Java是一个严格的面相对象(OOP)语言,面向对象方法的核心概念是将复杂的问题分解为较小的对象.具体的对象应该尽量做聚合抽象.得到共同的抽象父类.

一个对象有两个部分组成:状态和行为. 以一个高级点的自行车🚴‍♀️为例子:

  1. 状态: 速度、档位
  2. 行为: 换档、刹车

## 类和实例对象

类(Class)是对象(Object)的蓝图,在我们创建一个Object之前我们应该先定义一个Class.

我们可以将Class视为房子的草图(原型)。它包含有关地板,门,窗户等的所有详细信息。基于这些描述,我们建造了房屋。房子是Object

Class 语法格式

1
2
3
4
class ClassName {
// fields
// methods
}

fieldsmethods表示 Object的状态和行为.

  • fields - 用来保存数据
  • methods - 用于执行一些操作

以自行车为例子:

1
2
3
4
5
6
7
8
9
10
class Bicycle {

// state or field
private int gear = 5;

// behavior or method
public void braking() {
System.out.println("Working of Braking");
}
}

在上面的示例中,我们创建了一个名为Bicycle的类。它包含一个名为gear的字段和一个名为braking()的方法。

Object 对象用法

Class的实例被称为对象Object.例如,假设Bicycle是一个类,那么可以将MountainBicycle,SportsBicycle,TouringBicycle等视为该类的对象。

1
2
3
4
5
6
//实例对象创建格式: className object = new className();

// for Bicycle class
Bicycle sportsBicycle = new Bicycle();

Bicycle touringBicycle = new Bicycle();

我们将new关键字与类的构造函数一起使用来创建对象。构造函数与方法相似,并且与类具有相同的名称。

完整例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Lamp {

// stores the value for light
// true if light is on
// false if light is off
boolean isOn;

// method to turn on the light
void turnOn() {
isOn = true;
System.out.println("Light on? " + isOn);

}

// method to turnoff the light
void turnOff() {
isOn = false;
System.out.println("Light on? " + isOn);
}
}

class Main {
public static void main(String[] args) {

// create objects led and halogen
Lamp led = new Lamp();
Lamp halogen = new Lamp();

// turn on the light by
// calling method turnOn()
led.turnOn();

// turn off the light by
// calling method turnOff()
halogen.turnOff();
}
}

方法

方法: 将一组有逻辑关系的代码放在一起的语法形式. 执行这个方法其实就是执行方面里面的的代码.

在Java中有两种类型的方法:

  • 标准库的方法
  • 用户自定义的方法

标准库方法

标准库方法是Java中的内置方法,可以随时使用。这些标准库是和Java类库(JCL)在一起的,它们是以*.jar的文件形式存在JVMJRE中.

例如:

  • printjava.io.PrintSteam包中的一个方法.`print(“…”)方法是打印双引号内的字符串。
  • sqrt()Math类的静态方法. 它返回数字的平方根。
1
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args) {

// using the sqrt() method
System.out.print("Square root of 4 is: " + Math.sqrt(4));
}
}
// Square root of 4 is: 2.0

用户自定义方法

我们还可以创建自己选择的方法来执行某些任务。这种方法称为用户定义方法。

自定义方法例子:

1
2
3
public static void myMethod() {
System.out.println("My Function called");
}

上面例子我们创建了一个名为myMethod方法,在方面名之前我们使用了public(访问修饰符),static(类型),void(方法返回值类型).

  • public - 访问修饰符。这意味着可以从任何地方访问该方法
  • static - 访问修饰符。这意味着可以从任何地方访问该方法.PS.说明这个方法属于类.
  • void - 用来说明该方法没有返回值.

自定义方法完整格式:

1
2
3
modifier static returnType nameOfMethod (parameters) {
// method body
}
  • modifier - 定义方法的访问类型.
  • static - 通过static关键字,表明该方法通过类(Class)就可以访问,无需通过对象(Object).
  • returnType - 它指定方法返回的值的类型。例如,如果方法具有int返回类型,则它返回整数值。如果方法没有返回值则使用关键字void
  • nameOfMethod - 它是一个标识符,用于引用程序中的特定方法。
  • parameters (arguments) - 这些是传递给方法的值。我们可以将任意数量的参数传递给方法。
  • method body - 它包括用于执行某些任务的编程语句。The method body is enclosed inside the curly braces { }.

执行方法

Java中执行方法很简单:方法名+(parameters).

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Main {
public static void main(String[] args) {
System.out.println("About to encounter a method.");

// method call
myMethod();

System.out.println("Method was executed successfully!");
}

// method definition
private static void myMethod(){
System.out.println("Printing from inside myMethod()!");
}
}
// 输出
// About to encounter a method.
// Printing from inside myMethod().
// Method was executed successfully!

带返回值例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class SquareMain {
public static void main(String[] args) {
int result;

// call the method and store returned value
result = square();
System.out.println("Squared value of 10 is: " + result);
}

public static int square() {
// return statement
return 10 * 10;
}
}
// 输出
// Squared value of 10 is: 100

带参数并且有返回值的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Main {

public static void main(String[] args) {
int result, n;

n = 3;
result = square(n);
System.out.println("Square of 3 is: " + result);

n = 4;
result = square(n);
System.out.println("Square of 4 is: " + result);
}

// method
static int square(int i) {
return i * i;
}
}

方法重载

在Java中,如果两个或多个方法的名称相同但参数数量或参数的类型不同,则称这些方法是重载方法.

例子:

1
2
3
4
void func() { ... }
void func(int a) { ... }
float func(double a) { ... }
float func(int a, float b) { ... }

func是重载方法,因为它们参数不通.重载方法的返回值可能相同也可能不同,但它们的参数一定不同.

代码例子-参数数量不同:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MethodOverloading {
private static void display(int a){
System.out.println("Arguments: " + a);
}

private static void display(int a, int b){
System.out.println("Arguments: " + a + " and " + b);
}

public static void main(String[] args) {
display(1);
display(1, 4);
}
}
// 输出
// Arguments: 1
// Arguments: 1 and 4

代码例子-参数类型不同:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MethodOverloading {

// this method accepts int
private static void display(int a){
System.out.println("Got Integer data.");
}

// this method accepts String object
private static void display(String a){
System.out.println("Got String object.");
}

public static void main(String[] args) {
display(1);
display("Hello");
}
}
// 输出
// Got Integer data.
// Got String object.

构造函数

在Java中,每个类都有构造函数,在创建类的实例对象时会自动调用类的构造函数.构造函数类型于方法,但实际上它不是方法.

构造函数格式例子:

1
2
3
4
5
class Test {
Test() {
// constructor body
}
}

构造函数的名称和类名必须相同并且不能返回任何值.

不是构造函数例子:

1
2
3
4
5
class Test {
void Test() {
// method body
}
}

因为Test()虽然和类名相同,但它定义了返回值类型为void.则它是方法而不是构造函数.

代码例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Main {
private int x;

// constructor
private Main(){
System.out.println("Constructor Called");
x = 5;
}

public static void main(String[] args){
// constructor is called while creating object
Main obj = new Main();
System.out.println("Value of x = " + obj.x);
}
}
// 输出
// Constructor Called
// Value of x = 5

构造函数类型

在Java中构造函数有3种类型:

  • 无参数构造函数
  • 默认构造函数
  • 参数化构造函数

无参数构造函数

如果构造函数没有参数则称为无参数构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Company {
String domainName;

// public constructor
public Company(){
domainName = "programiz.com";
}
}

public class Main {

public static void main(String[] args) {

// object is created in another class
Company companyObj = new Company();
System.out.println("Domain name = "+ companyObj.domainName);
}
}

默认构造函数

如果在Java类中没有定义构造函数,则Java编译器会在运行时自动创建无参数构造函数.这个构造函数被称为默认构造函数. 默认构造函数会使用默认值,初始化所有未被初始化的实例变量.

变量类型 默认值
boolean false
byte 0
short 0
int 0
long 0L
char \u0000
float 0.0f
double 0.0d
object Reference null

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class DefaultConstructor {

int a;
boolean b;

public static void main(String[] args) {

// A default constructor is called
DefaultConstructor obj = new DefaultConstructor();

System.out.println("a = " + obj.a);
System.out.println("b = " + obj.b);
}
}
// 输出
// a = 0
// b = false

在上面代码中我们没有创建构造函数,在构建DefaultConstructor实例对象时,Java编译器自动给类中的实例变量设置了初始值.

参数化构造函数

参数化构造函数指有入参的构造函数.

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Vehicle {

int wheels;

// constructor accepting single value
private Vehicle(int wheels){
this.wheels = wheels;
System.out.println(wheels + " wheeler vehicle created.");
}

public static void main(String[] args) {

// calling the constructor by passing single value
Vehicle v1 = new Vehicle(2);
Vehicle v2 = new Vehicle(3);
Vehicle v3 = new Vehicle(4);
}
}
// 输出
// 2 wheeler vehicle created.
// 3 wheeler vehicle created.
// 4 wheeler vehicle created.

构造函数重载

和方法一样,构造函数是支持重载的.

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Company {

String domainName;

// constructor with no parameter
public Company(){
this.domainName = "default";
}

// constructor with single parameter
public Company(String domainName){
this.domainName = domainName;
}

public void getName(){
System.out.println(this.domainName);
}

public static void main(String[] args) {
// calling the constructor with no parameter
Company defaultObj = new Company();

// calling the constructor with single parameter
Company programizObj = new Company("programiz.com");

defaultObj.getName();
programizObj.getName();
}
}
// 输出
// default
// programiz.com

在上面例子中,有两个构造函数.它们的入参不同而已.在构建类的实例对象时会根据传参自动调用对应的构造函数.

构造函数需要注意事项

  • 构造函数在实例化时自动调用.
  • 构造函数的名称必须和类名一样并且没有返回类型.
  • 如果没有写构造函数,Java编译器会自动创建默认构造函数.并且在构造函数内自动对未初始化的实例变量进行赋值初始化.
  • 构造函数分为:无参构造函数、默认构造函数、参数化构造函数.
  • 构造函数不能是抽象(abstract)或是静态(static)或不可变(final).
  • 构造函数可以重载但不能重写.

字符串

在Java中字符串并不是基础数据类型.所有 字符串都是String类的对象.

1
2
// create a string
String type = "java programming";

在Java中通过""表示字符串.

⚠️所有字符串都是 String类的实例.

String 方法

方法名 描述
concat() 将两个字符串连接起来
equals() 比较两个字符串的值
charAt() 返回字符串指定的字符
getBytes() 将字符串转换成字节数组
indexOf() 返回字符串中指定字符的位置
length() 返回指定字符串的大小
replace() 将指定的旧字符串替换为指定的新字符
substring() 返回字符串的子字符串
split() 将字符串分成字符串数组
toLowerCase() 将字符串转成小写
toUpperCase() 将字符串转成大写
valueOf() 返回指定数据的字符串表示形式

获取字符串长度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Main {
public static void main(String[] args) {

// create a string
String greet = "Hello! World";
System.out.println("The string is: " + greet);

//checks the string length
System.out.println("The length of the string: " + greet.length());
}
}
// 输出
// The string is: Hello! World
// The length of the string: 12

通过length()获取字符串的长度.

将两个字符串合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Main {
public static void main(String[] args) {

// create string
String greet = "Hello! ";
System.out.println("First String: " + greet);

String name = "World";
System.out.println("Second String: " + name);

// join two strings
String joinedString = greet.concat(name);
System.out.println("Joined String: " + joinedString);
}
}
// 输出
// First String: Hello!
// Second String: World
// Joined String: Hello! World

在上面例子中,通过concat()将两个字符串连接成一个字符串.

+连接字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Main {
public static void main(String[] args) {

// create string
String greet = "Hello! ";
System.out.println("First String: " + greet);

String name = "World";
System.out.println("Second String: " + name);

// join two strings
String joinedString = greet + name;
System.out.println("Joined String: " + joinedString);
}
}
// 输出
// First String: Hello!
// Second String: World
// Joined String: Hello! World

比较两个字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Main {
public static void main(String[] args) {

// create strings
String first = "java programming";
String second = "java programming";
String third = "python programming";

// compare first and second strings
boolean result1 = first.equals(second);
System.out.println("Strings first and second are equal: " + result1);

//compare first and third strings
boolean result2 = first.equals(third);
System.out.println("Strings first and third are equal: " + result2);
}
}
// 输出
// Strings first and second are equal: true
// Strings first and third are equal: false

equals()方法比较两个字符串

也可以用==操作符和compareTo()方法比较两个字符串

获取一个字符串的字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Main {
public static void main(String[] args) {

// create string using the string literal
String greet = "Hello! World";
System.out.println("The string is: " + greet);

// returns the character at 3
System.out.println("The character at 3: " + greet.charAt(3));

// returns the character at 7
System.out.println("The character at 7: " + greet.charAt(7));
}
}
// 输出
// The string is: Hello! World
// The character at 3: l
// The character at 7: W

通过charAt()获取某个字符串的字符.

字符串其它常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Main {
public static void main(String[] args) {

// create string using the new keyword
String example = new String("Hello! World");

// returns the substring World
System.out.println("Using the subString(): " + example.substring(7));

// converts the string to lowercase
System.out.println("Using the toLowerCase(): " + example.toLowerCase());

// converts the string to uppercase
System.out.println("Using the toUpperCase(): " + example.toUpperCase());

// replaces the character '!' with 'o'
System.out.println("Using the replace(): " + example.replace('!', 'o'));
}
}
// 输出
// Using the subString(): World
// Using the toLowerCase(): hello! world
// Using the toUpperCase(): HELLO! WORLD
// Using the replace(): Helloo World

字符串是不可变的

Java中字符串如果创建成功则不能进行修改.将两个字符串合并的操作其实是内存中创建新的字符串.

1
2
3
4
// create a string
String example = "Hello";
// adds another string to the string
example = example.concat(" World");

上面👆代码的实际在内存中的执行步骤是:

  • 在JVM内存创建字符串”Hello”
  • 将创建的”Hello”引用给变量example
  • 在”Hello”后面添加” World”.
  • JVM在内存创建新的字符串”Hello World”
  • 将变量引用新创建的字符串”Hello World”.
  • 原来的字符串”Hello”并没有被修改

new关键字创建字符串

Java中可以用new String()创建字符串.

1
2
// create a string using the new keyword
String name = new String("java string");

用双引号和new String()的区别是:

在Java中,JVM维护一个字符串池以将其所有字符串存储在内存中。字符串池有助于重用字符串。

使用字符串文字创建字符串时,将直接提供字符串的值。因此,编译器首先检查字符串池以查看字符串是否已经存在。

  • 如果字符串存在,则JVM不会创建新字符串,而是将内存中存在的字符串引用给变量.
  • 如果字符串不存在, 则创建新的字符串.

但是如果new String()创建字符串,则直接创建新的字符串.

字符串中使用 双引号

如果我们需要在字符串中使用双引号"",则应该用在它们之前添加\.

1
2
// use the escape character
String example = "This is the \"String\" class.";

访问修饰符

访问修饰符用来控制:类,接口,变量,方法,构造函数,数据成员,和setter方法的可访问性.

Java中有四种访问修饰符

修饰符 描述
Default 声明仅在package中可见(包私有)
Private 声明仅在class中可见
Protected 声明在包或所有子类中均可见
Public 声明随处可见

default 修饰符

如果你没有指明:类,方法,变量等的访问修饰符.则它会被设置成default类型

1
2
3
4
5
6
package defaultPackage;
class Logger {
void message(){
System.out.println("This is a message");
}
}

Logger类被设定成default类型访问修饰符.所以Logger类只能在当前package访问.

private修饰符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Data {
// private variable
private String name;
}

public class Main {
public static void main(String[] main){

// create an object of Data
Data d = new Data();

// access private variable and field from another class
d.name = "Programiz";
}
}

在上面的示例中,我们声明了一个名为name的私有变量和一个名为display()的私有方法。运行该程序时,将出现以下错误:

1
2
Main.java:18: error: name has private access in Data
d.name = "Programiz";

生成错误是因为我们试图从Main类访问私有变量和Data类的私有方法。

您可能想知道我们是否需要访问这些私有变量。在这种情况下,我们可以使用getters和setters方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Data {
private String name;

// getter method
public String getName() {
return this.name;
}
// setter method
public void setName(String name) {
this.name= name;
}
}
public class Main {
public static void main(String[] main){
Data d = new Data();

// access the private variable using the getter and setter
d.setName("Programiz");
System.out.println(d.getName());
}
}
// 输出
// The name is Programiz

在上面例子中,通过getName()setName()访问和操作变量,我们称这些方法为settergetter

Java中无法将 类和接口定义为private, 但是嵌套的类和接口是可以用private修饰.

protected访问修饰符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Animal {
// protected method
protected void display() {
System.out.println("I am an animal");
}
}

class Dog extends Animal {
public static void main(String[] args) {

// create an object of Dog class
Dog dog = new Dog();
// access protected method
dog.display();
}
}
// 输出
// I am an animal

在上面的示例中,我们在Animal类中有一个名为display()的受保护方法。 Animal类由Dog类继承。

由于可以从子类访问受保护的方法,因此我们可以从Dog类访问Animal类的方法。

Java中类和接口无法用 protected修饰

public 修饰符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Animal.java file
// public class
public class Animal {
// public variable
public int legCount;

// public method
public void display() {
System.out.println("I am an animal.");
System.out.println("I have " + legCount + " legs.");
}
}

// Main.java
public class Main {
public static void main( String[] args ) {
// accessing the public class
Animal animal = new Animal();

// accessing the public variable
animal.legCount = 4;
// accessing the public method
animal.display();
}
}
// 输出
// I am an animal.
// I have 4 legs.
  • 可从Main类访问公共类Animal
  • 可从Main类访问公共变量legCount
  • 公共方法display()是从Main类访问的。

package

包只是将相关类型(Java类,接口,枚举和注释)分组的容器。例如,在核心Java中,ResultSet接口属于java.sql包。该软件包包含SQL查询和数据库连接所需的所有相关类型。

如果要在代码中使用ResultSet接口,则只需导入java.sql包.

package是类,接口等容器.包可帮助您保留类名称空间并创建可维护的代码。

例如:在Java中有两个Date类.然而在Java编程语言中在一个Java项目只能有唯一的类名.

在JDK中两个Date属于不同的包.

  • java.util.Date - 这是一个普通的Date类,可以在任何地方使用。
  • java.sql.Date - 这是用于SQL查询等的SQL日期。

在Java中根据包是否是用户自己定义的,可以将包分为两种类型

内建包 (Built-in Package)

内建包是随附在JDK中的Java软件包.例如:java.lang,java.util,java.io

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.ArrayList;

class ArrayListUtilization {
public static void main(String[] args) {

ArrayList<Integer> myList = new ArrayList<>(3);
myList.add(3);
myList.add(2);
myList.add(1);

System.out.println(myList);
}
}
// 输出
// myList = [3, 2, 1]

ArrayList类属于java.util包.使用该类可以先用import关键字引入.

1
import java.util.ArrayList;

用户自定义包(User-defined Package)

Java还允许根据需要自己创建软件包。这些软件包称为用户定义的软件包。

定义Java package

1
package packageName;

Java使用文件系统目录来存储软件包.

1
2
3
└── com
└── test
└── Test.java

现在,编辑Test.java文件,并在文件的开头,将package语句写为:

1
2
3
4
5
6
7
8
9
package com.test;

class Test {
public static void main(String[] args){
System.out.println("Hello World!");
}
}
// 输出
// Hello World!

上面例子中,Test类属于com.test包.

包名约定

软件包名称必须唯一(如域名)。因此,有一种约定将包创建为域名,但顺序相反。例如com.company.name

在这里,包的每个级别都是文件系统中的目录。

1
2
3
└── com
└── company
└── name

而且,您可以创建多少个子目录(包层次结构)没有限制。

Java中导入包

Java中可以通过import表达式将整个包导入或只导入包中的类和接口.

1
2
3
4
5
import package.name.ClassName;   // To import a certain class only
import package.name.* // To import the whole package

import java.util.Date; // imports only Date class
import java.io.*; // imports everything inside java.io package

import声明在Java中可选的.

如果你想使用包中的类或者接口,可以直接使用全路径名称, 包括完整的程序包层次结构.

使用import声明包:

1
2
3
4
5
import java.util.Date;

class MyClass implements Date {
// body
}

使用全路径:

1
2
3
class MyClass implements java.util.Date {
//body
}

包声明和导入包

假设您定义了一个包com.programiz,其中包含一个类Helper

1
2
3
4
5
6
7
package com.programiz;

public class Helper {
public static String getFormattedDollar (double value){
return String.format("$%.2f", value);
}
}

现在,您可以将com.programiz包中的Helper类导入到您的实现类中。导入后,可以直接通过其名称引用该类。

1
2
3
4
5
6
7
8
9
10
11
12
import com.programiz.Helper;

class UseHelper {
public static void main(String[] args) {

double value = 99.5;
String formattedValue = Helper.getFormattedDollar(value);
System.out.println("formattedValue = " + formattedValue);
}
}
// 输出
// formattedValue = $99.50
  1. Helper类定义在 com.programiz包中.
  2. Helper类被导入到不同的文件中.该文件包含UseHelper类.
  3. UseHelper类内部调用Helper类的getFormattedDollar()方法.

Java中import声明写在package之后和类定义之前.

1
2
3
4
5
6
package package.name;
import package.ClassName; // only import a Class

class MyClass {
// body
}

This 关键字

在Java中,此关键字用于引用方法或构造函数中的当前对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Main {
int instVar;

Main(int instVar){
this.instVar = instVar;
System.out.println("this reference = " + this);
}

public static void main(String[] args) {
Main obj = new Main(8);
System.out.println("object reference = " + obj);
}
}
// 输出
//this reference = com.ThisAndThat.MyClass@74a14482
// object reference = com.ThisAndThat.MyClass@74a14482

在上面例子中, 根据类Main创建了一个实例对象obj.然后打印出了对象obj和该类关键字this的一个引用.

从打印信息可以看出,对象obj和关键字this都是对当前的对象一样.

使用关键字 this

java中,是不允许在一个作用域内定义两个或多个名字相同的变量的.但是,实例变量和参数可能具有相同的名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Main {

int age;
Main(int age){
age = age;
}

public static void main(String[] args) {
Main obj = new Main(8);
System.out.println("obj.age = " + obj.age);
}
}

// 输出
// obj.age = 0

在上面的程序中,实例变量和参数具有相同的名称:age。在这里,由于名称不明确,Java编译器感到困惑。

在这种情况下,我们应该使用this关键字.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Main {

int age;
Main(int age){
this.age = age;
}

public static void main(String[] args) {
Main obj = new Main(8);
System.out.println("obj.age = " + obj.age);
}
}
// 输出
// obj.age = 8

现在,我们正在获得预期的输出。这是因为在调用构造函数时,构造函数内部的该对象已被调用了该构造函数的对象obj代替。因此,将变量age分配为值8。

另外,如果参数和实例变量的名称不同,则编译器会自动附加此关键字。

1
2
3
4
5
6
7
class Main {
int age;

Main(int i) {
age = i;
}
}

等同于:

1
2
3
4
5
6
7
class Main {
int age;

Main(int i) {
this.age = i;
}
}

this与Getters和Setters

this的另一个常见用法是在类的setter和getters方法中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Main {
String name;

// setter method
void setName( String name ) {
this.name = name;
}

// getter method
String getName(){
return this.name;
}

public static void main( String[] args ) {
Main obj = new Main();

// calling the setter and the getter method
obj.setName("Toshiba");
System.out.println("obj.name: "+obj.getName());
}
}
// 输出
// obj.name: Toshiba
  • this在setter设置值.
  • this在getter读取值.

this 与 构造函数重载

在处理构造函数重载时,我们可能不得不从另一个构造函数调用一个构造函数。在这种情况下,我们不能显式调用构造函数。相反,我们必须使用this关键字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class Complex {

private int a, b;

// constructor with 2 parameters
private Complex( int i, int j ){
this.a = i;
this.b = j;
}

// constructor with single parameter
private Complex(int i){
// invokes the constructor with 2 parameters
this(i, i);
}

// constructor with no parameter
private Complex(){
// invokes the constructor with single parameter
this(0);
}

@Override
public String toString(){
return this.a + " + " + this.b + "i";
}

public static void main( String[] args ) {

// creating object of Complex class
// calls the constructor with 2 parameters
Complex c1 = new Complex(2, 3);

// calls the constructor with a single parameter
Complex c2 = new Complex(3);

// calls the constructor with no parameters
Complex c3 = new Complex();

// print objects
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
}
}

输出:

1
2
3
2 + 3i
3 + 3i
0 + 0i

在上面例子中,我们使用this关键字.

  • 在构造函数Complex(int i)中调用构造函数Complex(int i, int y)
  • 在构造函数Complex()中调用构造函数Complex(int i)

在调用System.out.println(c1);时,对象会被转换成字符串,这个转换过程是调用对象内的toString()方法完成.由于我们在类内部重写了toString()方法,因此我们根据该方法获得输出。

使用this()可以减少我们代码量,但我们应谨慎使用this().因为在一个构造函数调用另一个构造函数会增加开销,且性能不是很好.

注意:从一个构造函数调用另一个构造函数称为显式构造函数调用。

this作为参数传递

我们可以将this当前对象作为参数传递给方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class ThisExample {
// declare variables
int x;
int y;

ThisExample(int x, int y) {
// assign values of variables inside constructor
this.x = x;
this.y = y;

// value of x and y before calling add()
System.out.println("Before passing this to addTwo() method:");
System.out.println("x = " + this.x + ", y = " + this.y);

// call the add() method passing this as argument
add(this);

// value of x and y after calling add()
System.out.println("After passing this to addTwo() method:");
System.out.println("x = " + this.x + ", y = " + this.y);
}

void add(ThisExample o){
o.x += 2;
o.y += 2;
}
}

class Main {
public static void main( String[] args ) {
ThisExample obj = new ThisExample(1, -2);
}
}

输出:

1
2
3
4
Before passing this to addTwo() method:
x = 1, y = -2
After passing this to addTwo() method:
x = 3, y = 0

ThisExample()中将this作为参数传给add方法.由于此this包含对类的对象obj的引用.

Final 关键字

Java中final关键字被用来表示常量.它可以用在:变量、方法、类.

如果实体(类,方法,变量)被声明了final,则它只能被赋值一次.

  • final声明的变量如果赋值则不能再被赋值.
  • final声明的方法不能被重写.
  • final声明的类不能继承.

final 变量

Java中不能修改已经声明final变量的值.

1
2
3
4
5
6
7
8
9
10
11
class Main {
public static void main(String[] args) {

// create a final variable
final int AGE = 32;

// try to change the final variable
AGE = 45;
System.out.println("Age: " + AGE);
}
}

在上面的程序中,我们创建了一个名为age的变量。并且我们尝试更改最终变量的值。

运行程序时,将出现以下错误消息,提示编译错误。

1
2
3
cannot assign a value to final variable AGE
AGE = 45;
^

注意:建议使用大写形式在Java中声明最终变量。

final 方法

Java中被final声明的方法不能被它的子类重写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class FinalDemo {
// create a final method
public final void display() {
System.out.println("This is a final method.");
}
}

class Main extends FinalDemo {
// try to override final method
public final void display() {
System.out.println("The final method is overridden.");
}

public static void main(String[] args) {
Main obj = new Main();
obj.display();
}
}

在上面的示例中,我们在FinalDemo类中创建了一个名为display()的最终方法。在这里,Main类继承了FinalDemo类。

我们尝试在子类重写父类final声明的方法.在运行的时候会报错:

1
2
3
4
display() in Main cannot override display() in FinalDemo
public final void display() {
^
overridden method is final

final 类

final声明的类不能被继承.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// create a final class
final class FinalClass {
public void display() {
System.out.println("This is a final method.");
}
}

// try to extend the final class
class Main extends FinalClass {
public void display() {
System.out.println("The final method is overridden.");
}

public static void main(String[] args) {
Main obj = new Main();
obj.display();
}
}

在上面的示例中,我们创建了被final声明的类FinalClass。在这里,我们试图通过Main类继承它。

当运行程序时会报错:

1
2
3
cannot inherit from final FinalClass
class Main extends FinalClass {
^

递归

Java中调用自身的方法称为递归方法,这个过程被称为递归.

递归逻辑:

在上面的示例中,我们从·main方法内部调用了recurse()方法。 (正常方法调用)。并且,在recurse()方法内部,我们再次调用相同的recurse方法。这是一个递归调用。

为了停止递归调用,我们需要在方法内部提供一些条件。否则,该方法将被无限调用。

数字的阶乘的递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Factorial {

static int factorial( int n ) {
if (n != 0) // termination condition
return n * factorial(n-1); // recursive call
else
return 1;
}

public static void main(String[] args) {
int number = 4, result;
result = factorial(number);
System.out.println(number + " factorial = " + result);
}
}

输出;

1
4 factorial = 24

在上面的示例中,我们有一个名为factorial()的方法。从main()方法调用factorial()。用传递的数字变量作为参数。用传递的数字变量作为参数.

执行逻辑:

递归的优缺点

进行递归调用时,将在堆栈上分配新的变量存储位置。随着每个递归调用的返回,旧的变量和参数将从堆栈中删除。因此,递归通常使用更多的内存,并且通常很慢。

另一方面,递归解决方案要简单得多,并且花费更少的时间来编写,调试和维护。

instanceof

Java中instanceof关键字是个二元操作符.它用于检查对象是否是特定类的实例。

instanceof也可以检查对象是否是实现接口的类的实例.

1
result = objectName instanceof className;

instanceof运算符的左操作数是对象名称,右操作数是类名称。如果对象是类的实例,则结果为true,否则为false。

例子:

1
2
3
4
5
6
7
8
9
class Main {
public static void main (String[] args) {
String name = "Programiz";
Integer age = 22;

System.out.println("Is name an instance of String: "+ (name instanceof String));
System.out.println("Is age an instance of Integer: "+ (age instanceof Integer));
}
}

输出:

1
2
Is name an instance of String: true
Is age an instance of Integer: true

上面例子中,在对象上创建了字段nameString类型和Integer类型的字段age.当用instanceof操作符检测name字段是否是String和检测age字段是否是Integer类型.

instanceof在继承中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Animal {
}

// Dog class is a subclass of Animal
class Dog extends Animal {
}

class Main {
public static void main(String[] args){
Dog d1 = new Dog();

// checks if d1 is an object of Dog
System.out.println("Is d1 an instance of Dog: "+ (d1 instanceof Dog));

// checks if d1 is an object of Animal
System.out.println("Is d1 an instance of Animal: "+ (d1 instanceof Animal));
}
}

输出:

1
2
Is d1 is an instance of Dog: true
Is d1 an instance of Animal: true

在上面的示例中,d1DogAnimal类的实例。因此,Dogd1实例和Animald1实例都为true。

Object Class

Java中所有的类都继承至Object类.在Object类的继承过程中不使用extend关键字。这种继承隐式发生在Java中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Animal {
}

class Dog {
}

class Cat {
}
class Main {
public static void main(String[] args) {
Dog d1 = new Dog();
Animal a1 = new Animal();
Cat c1 = new Cat();

System.out.println("Is d1 an instance of the Object class: "+ (d1 instanceof Object));
System.out.println("Is a1 an instance of the Object class: "+ (a1 instanceof Object));

System.out.println("Is c1 an instance of the Object class: "+ (c1 instanceof Object));
}
}

输出:

1
2
3
Is d1 an instance of the Object class: true
Is a1 an instance of the Object class: true
Is c1 an instance of the Object class: true

Object向上转换和向下转换

在Java中,子类的对象可以视为超类的对象。这称为向上转换。Java编译器自动执行向上转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Animal {
public void displayInfo() {
System.out.println("I am an animal.");
}
}

class Dog extends Animal {
}

class Main {
public static void main(String[] args) {
Dog d1 = new Dog();
Animal a1 = d1;
a1.displayInfo();
}
}

输出:

1
I am an animal.

在上面的示例中,我们创建了Dog类的对象d1。我们使用该d1对象创建Animal类的对象a1。在Java中这称为转换。

该代码执行没有任何问题。这是因为上转换是由Java编译器自动完成的。

向下转换是向上转换的反向

在向下转换的情况下,超类的对象被视为子类的对象。我们必须明确指示编译器以Java向下转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Animal {
}

class Dog extends Animal {
public void displayInfo() {
System.out.println("I am a dog.");
}
}

class Main {
public static void main(String[] args) {
Animal a1 = new Animal();
Dog d1 = (Dog)a1; // Downcasting

d1.displayInfo();
}
}

当我们运行程序时,我们将获得一个名为ClassCastException的异常。

在这里,我们创建了超类Animal的对象a1。然后,我们尝试将a1对象转换为子类Dog的对象d1

这引起了问题。这是因为超类Animala1对象也可能引用其他子类。如果我们与Dog一起创建另一个子类Cat;动物可能是Cat,也可能是Dog,引起歧义。

要解决此问题,我们可以使用instanceof运算符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Animal {
}

class Dog extends Animal {
public void displayInfo() {
System.out.println("I am a dog");
}
}

class Main {
public static void main(String[] args) {
Dog d1 = new Dog();
Animal a1 = d1; // Upcasting

if (a1 instanceof Dog){
Dog d2 = (Dog)a1; // Downcasting
d2.displayInfo();
}
}
}

输出:

1
I am a dog

在上面的示例中,我们使用instanceof运算符检查a1对象是否是Dog类的实例。仅当的表达式a1 instanceof Dogtrue时,才进行向下转换。

instanceof of Interface

instanceof运算符还用于检查类的对象是否也是实现该类的接口的实例。

1
2
3
4
5
6
7
8
9
10
11
12
interface Animal {
}

class Dog implements Animal {
}

class Main {
public static void main(String[] args) {
Dog d1 = new Dog();
System.out.println("Is d1 an instance of Animal: "+(d1 instanceof Animal));
}
}

输出:

1
Is d1 an instance of Animal: true

在上面的示例中,我们创建了一个实现Animal接口的Dog类。

然后,创建Dog类的d1对象。我们已使用instanceof运算符检查d1对象是否也是Animal接口的实例。

继承

继承是OOP(面向对象编程)的关键功能之一,它使我们能够从现有类中定义一个新类。

1
2
3
4
5
6
7
8
9
class Animal
{
// eat() method
// sleep() method
}
class Dog extends Animal
{
// bark() method
}

在Java中,我们使用extends关键字从类继承。在这里,我们从Animal类继承了Dog类。

Animal是超类(父类、基础类),Dog是一个子类(派生类).子类继承父类的字段和方法.

继承是一种关系的连接.我们将两个类用继承模式连接起来,是因为这个类是存在实际的逻辑或业务的联系.例如:

  • 汽车是交通工具
  • 橘子是水果
  • 外科医生是医生
  • 狗狗是动物
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Animal {

public void eat() {
System.out.println("I can eat");
}

public void sleep() {
System.out.println("I can sleep");
}
}

class Dog extends Animal {
public void bark() {
System.out.println("I can bark");
}
}

class Main {
public static void main(String[] args) {

Dog dog1 = new Dog();

dog1.eat();
dog1.sleep();

dog1.bark();
}
}

输出:

1
2
3
I can eat
I can sleep
I can bark

在这里,我们从超类Animal继承了一个子类DogDog类从Animal类继承了eat()sleep()方法。

因此,Dog类的对象可以访问Dog类和Animal类的成员。

访问控制

Class Package subclass World
public Yes Yes Yes Yes
private Yes No No No
protected Yes Yes Yes No
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Animal {
protected String type;
private String color;

public void eat() {
System.out.println("I can eat");
}

public void sleep() {
System.out.println("I can sleep");
}

public String getColor(){
return color;
}

public void setColor(String col){
color = col;
}
}

class Dog extends Animal {
public void displayInfo(String c){
System.out.println("I am a " + type);
System.out.println("My color is " + c);
}
public void bark() {
System.out.println("I can bark");
}
}

class Main {
public static void main(String[] args) {

Dog dog1 = new Dog();
dog1.eat();
dog1.sleep();
dog1.bark();

dog1.type = "mammal";
dog1.setColor("black");
dog1.displayInfo(dog1.getColor());
}
}

输出:

1
2
3
4
5
I can eat
I can sleep
I can bark
I am a mammal
My color is black

type字段在Animal类中是protected.但我们可以用Main类中使用.

1
dog1.type = "mammal";

因为这个两个类是在一个package中.

继承规则

Java不支持通过类的多重继承和混合继承。但是,我们可以通过接口在Java中实现多重继承。

继承好处

  • 最重要的用途是代码的可重用性。父类中存在的代码无需在子类中再次编写.
  • 通过方法重写实现运行时多态.

方法重写

如果在超类类和子类类中都定义了相同的方法,则子类类的方法将覆盖超类的方法。这种场景被称为方法重写.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Animal {
public void displayInfo() {
System.out.println("I am an animal.");
}
}

class Dog extends Animal {
@Override
public void displayInfo() {
System.out.println("I am a dog.");
}
}

class Main {
public static void main(String[] args) {
Dog d1 = new Dog();
d1.displayInfo();
}
}
// 输出
// I am a dog.

在上面的程序中,Animal超类和Dog子类中都存在displayInfo()方法。

d1对象调用displayInfo()方法,该方法在子类Dog中存在,则触发Dog中的displayInfo()方法.该方法displayInfo()在子类和父类都存在,但子类的方法覆盖了父类方法.

请注意,在我们的示例中使用了@Override注释。在Java中,注释是我们用于向编译器提供信息的元数据。请注意,在我们的示例中使用了@Override注释。在Java中,注释是我们用于向编译器提供信息的元数据。

使用@Override不是强制性的。但是,当我们使用此方法时,该方法应遵循所有覆盖规则。否则,编译器将生成错误。

Java 重写规则

  • 超类和子类都必须具有相同的方法名称,相同的返回类型和相同的参数列表

  • 我们不能覆盖声明为finalstatic的方法

  • 我们应该始终重写超类的抽象方法(将在以后的教程中进行讨论)

super 关键字在Java重写

在Java中执行覆盖时出现的一个常见问题是:

覆盖后可以访问父类的方法吗?

答案是肯定的。要从子类访问超类的方法,我们使用super关键字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Animal {
public void displayInfo() {
System.out.println("I am an animal.");
}
}

class Dog extends Animal {
public void displayInfo() {
super.displayInfo();
System.out.println("I am a dog.");
}
}

class Main {
public static void main(String[] args) {
Dog d1 = new Dog();
d1.displayInfo();
}
}

// 输出
// I am an animal.
// I am a dog.

在上面的示例中,子类Dog重写了超类AnimaldisplayInfo()方法。

当我们使用Dog子类的d1对象调用displayInfo()方法时,将调用Dog子类内部的方法,不会调用父类内部的方法。在Dog子类的displayInfo()内部,我们使用了super.displayInfo()来调用超类的displayInfo()

注意: Java中构造函数是不会被继承的.所以不存在构造函数的重写.但是,我们可以从其子类构造函数通过super调用父类的构造函数。

重写中的访问修饰符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Animal {
protected void displayInfo() {
System.out.println("I am an animal.");
}
}

class Dog extends Animal {
public void displayInfo() {
System.out.println("I am a dog.");
}
}

class Main {
public static void main(String[] args) {
Dog d1 = new Dog();
d1.displayInfo();
}
}
// 输出
// I am a dog

在上面的示例中,子类Dog重写了父类AnimaldisplayInfo()方法。

每当我们使用d1(子类的对象)调用displayInfo()时,就会调用子类内部的方法。

注意,在Animal父类中将displayInfo()声明为受protected的。相同的方法在Dog子类中具有公共访问说明符。这是可以的,因为public提供了比受protected者更大的访问权限。

重写抽象方法

在Java中,抽象类被创建为其他类的超类。并且,如果类包含抽象方法,则必须重写它。

super 关键字

Java中的super关键字在子类中用于访问父类成员(属性,构造函数和方法)。

使用super关键字

  1. 调用在子类中重写的父类的方法。
  2. 如果父类和子类都具有相同名称的属性,则访问父类的属性(字段)。
  3. 从子类构造函数中显式调用父类no-arg(默认值)或参数化构造函数。

父类的访问重写方法

如果在父类子类中都定义了相同名称的方法,则子类中的方法将覆盖父类中的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Animal {

// overridden method
public void display(){
System.out.println("I am an animal");
}
}

class Dog extends Animal {

// overriding method
@Override
public void display(){
System.out.println("I am a dog");
}

public void printMessage(){
display();
}
}

class Main {
public static void main(String[] args) {
Dog dog1 = new Dog();
dog1.printMessage();
}
}
// 输出
// I am a dog

在此示例中,通过使对象Dog1Dog类,我们可以调用其方法printMessage(),然后执行display()语句。

由于在这两个类中都定义了display(),所以Dog子类的方法将覆盖超类Animal的方法。因此,将调用子类的display()

如果必须调用父类的被重写方法怎么办?

如果需要调用超类Animal的重写方法display(),则使用super.display()

通过super调用父类重写的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Animal {

// overridden method
public void display(){
System.out.println("I am an animal");
}
}

class Dog extends Animal {

// overriding method
@Override
public void display(){
System.out.println("I am a dog");
}

public void printMessage(){

// this calls overriding method
display();

// this calls overridden method
super.display();
}
}

class Main {
public static void main(String[] args) {
Dog dog1 = new Dog();
dog1.printMessage();
}
}
// 输出
// I am a dog
// I am animal

工作流程是这样的:

父类访问修饰符

父类和子类可以具有相同名称的属性。我们使用super关键字来访问父类的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Animal {
protected String type="animal";
}

class Dog extends Animal {
public String type="mammal";

public void printType() {
System.out.println("I am a " + type);
System.out.println("I am an " + super.type);
}
}

class Main {
public static void main(String[] args) {
Dog dog1 = new Dog();
dog1.printType();
}
}
// 输出
// I am a mammal
// I am an animal

在此示例中,我们在父类Animal和子类Dog中都定义了相同的实例字段类型。

然后,我们创建·Dog类的对象dog1。然后,使用该对象调用printType()方法。

printType()方法内部:

  • type是指子类Dog的属性.
  • super.type引用是父类Animal属性.

使用super()访问父类构造函数

当一个class被创建时,默认构造函数会被自动调用.如果没有指定的话.

在子类构造函数中,我们可以通过super()调用父类的构造函数.

super()只能存在于子类的构造函数并且只能在第一行.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Animal {

// default or no-arg constructor of class Animal
Animal() {
System.out.println("I am an animal");
}
}

class Dog extends Animal {

// default or no-arg constructor of class Dog
Dog() {

// calling default constructor of the superclass
super();

System.out.println("I am a dog");
}
}

class Main {
public static void main(String[] args) {
Dog dog1 = new Dog();
}
}
// 输出
// I am an animal
// I am a dog

在这里,创建Dog类的对象dog1时,它将自动调用该类的默认或无参数构造函数。

在子类构造函数中,super()语句调用超类的构造函数并在其中执行该语句。因此输出I am an animal

然后,程序流返回到子类构造函数,并执行其余的语句。因此I am a dog被打印出来。

super()会被自动调用,而我们需要写冗余代码呢?
在子类中调用父类的带参构造函数是必须声明的,并且要在构造函数第一行,否则会报错.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Animal {

// default or no-arg constructor
Animal() {
System.out.println("I am an animal");
}

// parameterized constructor
Animal(String type) {
System.out.println("Type: "+type);
}
}

class Dog extends Animal {

// default constructor
Dog() {

// calling parameterized constructor of the superclass
super("Animal");

System.out.println("I am a dog");
}
}

class Main {
public static void main(String[] args) {
Dog dog1 = new Dog();
}
}
// 输出
// Type: Animal
// I am a dog

编译器可以自动调用no-arg构造函数。但是,它不能调用参数化的构造函数。

如果必须调用参数化的构造函数,则需要在子类构造函数中显式定义它。

象类和方法

抽象类是无法实例化的类(我们无法创建抽象类的对象)。在Java中,我们使用abstract关键字声明一个抽象类。

1
2
3
abstract class Animal {
//attributes and methods
}

如果尝试创建抽象类的对象,则会出现编译错误。

1
2
3
Animal a1 = new Animal()

Animal is abstract; cannot be instantiated

尽管抽象类无法实例化,但我们可以从中创建子类。我们可以创建子类的对象来访问抽象类的成员。

抽象方法

我们使用相同的关键字abstract来创建抽象方法。声明了一个抽象方法而没有实现。

1
abstract void makeSound();

在这里,makeSound()是一种抽象方法。 makeSound()的方法体替换为;

重要的是要注意,只有抽象类才能包含抽象方法。如果我们在不是抽象的类中包含抽象方法,则会出现错误。

抽象类可以包含抽象方法和非抽象方法。

1
2
3
4
5
6
7
abstract class Animal {
public void displayInfo() {
System.out.println(“I am an animal.”);
}

abstract void makeSound();
}

在上面的示例中,我们创建了一个抽象类Animal。它包含一个抽象方法makeSound()和一个非抽象方法displayInfo()

继承抽象类

抽象类无法实例化。要访问抽象类的成员,我们必须继承它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
abstract class Animal {
public void displayInfo() {
System.out.println("I am an animal.");
}
}

class Dog extends Animal {

}
class Main {
public static void main(String[] args) {
Dog d1 = new Dog();
d1.displayInfo();
}
}
// 输出
// I am an animal.

在上面的示例中,我们创建了一个抽象类Animal。我们无法创建Animal对象。为了访问AnimaldisplayInfo(),我们继承了Animal的子类Dog

然后,我们使用Dogd1对象访问方法displayInfo()

重写抽象方法

在Java中,必须在子类中重写父类的抽象方法。这是因为子类继承了父类的抽象方法。

由于我们的子类包含抽象方法,因此我们需要重写它们。

注意:如果子类也被声明为抽象,则不必强制重写抽象方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
abstract class Animal {
abstract void makeSound();

public void eat() {
System.out.println("I can eat.");
}
}

class Dog extends Animal {

public void makeSound() {
System.out.println("Bark bark");
}
}
class Main {
public static void main(String[] args) {
Dog d1 = new Dog();
d1.makeSound();
d1.eat();
}
}
// 输出
// Bark bark.
// I can eat.

在上面的示例中,我们创建了一个抽象类Animal。该类包含一个抽象方法makeSound()和一个非抽象方法eat()

我们从父类Animal继承了一个子类Dog。在这里,子类Dog提供了抽象方法makeSound()的实现。

然后,我们创建了Dog的对象d1。然后使用该对象调用d1.makeSound()d1.eat()方法。

访问抽象类的构造方法

与非抽象类相似,可以使用super关键字从子类访问抽象类的构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
abstract class Animal {
Animal() {
….
}
}

class Dog extends Animal {
Dog() {
super();
...
}
}

在这里,我们使用Dog的构造函数内部的super()访问Animal的构造函数。

为什么使用 抽象

抽象是面向对象编程的重要概念。抽象仅显示所需的信息,所有不必要的细节均被隐藏。这使我们能够通过使用更简单,更高级的想法省略或隐藏细节来管理复杂性。

抽象的实际示例可以是摩托车制动器。我们知道刹车的作用。当我们踩刹车时,摩托车将停止。但是,制动器的工作对我们而言是隐藏的。

隐藏制动器工作的主要优点是,现在制造商可以为不同的摩托车实施不同的制动器,但是制动器的作用是相同的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
abstract class Animal {
abstract void makeSound();

}

class Dog extends Animal {
public void makeSound() {
System.out.println("Bark bark.");
}
}

class Cat extends Animal {
public void makeSound() {
System.out.println("Meows ");
}
}

class Main {
public static void main(String[] args) {
Dog d1 = new Dog();
d1.makeSound();

Cat c1 = new Cat();
c1.makeSound();
}
}
// 输出
// Bark bark
// Meows

注意事项:

  • 我们使用abstract关键字创建抽象类和方法。
  • 抽象方法没有任何实现(方法体)。
  • 包含抽象方法的类也应该是抽象的。
  • 我们不能创建抽象类的对象。
  • 为了实现抽象类的功能,我们从其继承子类并创建该子类的对象。
  • 子类必须重写抽象类的所有抽象方法。但是,如果子类被声明为抽象的,则不必强制重写抽象方法。
  • 我们可以使用抽象类的引用来访问抽象类的静态属性和方法。

接口

接口是一个完全抽象的类,其中包括一组没有主体的方法。

Java中,接口定义了其他类必须实现的一组规范。

1
2
3
interface Language {
public void getName();
}

用关键字interface定义了接口Language,该接口内部指定了方法getName().

每个使用Language接口的类都需要实现getName()方法.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// create an interface
interface Language {
void getName(String name);
}

// class implements interface
class ProgrammingLanguage implements Language {

// implementation of abstract method
public void getName(String name) {
System.out.println("Programming Language: " + name);
}
}

class Main {
public static void main(String[] args) {
ProgrammingLanguage language = new ProgrammingLanguage();
language.getName("Java");
}
}
// 输出
// Programming Language: Java

在上面的示例中,我们创建了一个名为Language的接口。该接口包括一个抽象方法getName()

在这里,ProgrammingLanguage类实现了接口并提供了该方法的实现。

在接口内声明抽象方法时,不必使用abstract关键字。因为接口仅包含抽象方法,而没有常规方法。

注意:接口内的所有方法都是隐式公共的,而所有字段都是隐式公共的static final

1
2
3
4
5
6
7
8
interface Language {

// by default public static final
String type = "programming language";

// by default public
void getName();
}

实现接口

像抽象类一样,我们无法为接口创建对象。但是我们可以实现一个接口。我们通过implements关键字实现接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// create an interface
interface Polygon {
void getArea(int length, int breadth);
}

// implement the Polygon interface
class Rectangle implements Polygon {

// implementation of abstract method
public void getArea(int length, int breadth) {
System.out.println("The area of the rectangle is " + (length * breadth));
}
}

class Main {
public static void main(String[] args) {
// create an object
Rectangle r1 = new Rectangle();
r1.getArea(5, 6);
}
}
// 输出
// The area of the rectangle is 30

**注意:**一个类可以实现多个接口

1
2
3
4
5
6
7
8
9
10
11
12
interface A {
// members of A
}

interface B {
// members of B
}

class C implements A, B {
// abstract members of A
// abstract members of B
}

扩展接口

与类相似,接口可以扩展其他接口。 extend关键字用于扩展接口。

1
2
3
4
5
6
7
8
9
interface Line {
// members of Line interface
}

// extending interface
interface Polygon extends Line {
// members of Polygon interface
// members of Line interface
}

Polygon接口扩展了Line接口,则如果有类实现Polygon接口则它也需实现PolygonLine接口的方法.

**注意:**接口是可以多层扩展的

1
2
3
4
5
6
7
8
9
10
interface A {
...
}
interface B {
...
}

interface C extends A, B {
...
}

接口的优点

  • 接口提供了类(实现它)必须遵循的规范。
  • 与抽象类相似,接口可帮助我们实现Java抽象。
  • 接口还用于在Java中实现多重继承。

接口中的默认方法

随着Java 8的发布,我们现在可以在接口内部添加具有实现的方法。这些方法称为默认方法。

要在接口内部声明默认方法,我们使用default关键字。

1
2
3
public default void getSides() {
// body of getSides()
}

为什么需要 默认方法

用一个场景解释:

假设我们需要在接口中添加一个新方法。

我们可以轻松地在接口中添加该方法,而无需执行。但是,这还不是故事的结局。我们实现该接口的所有类都必须提供该方法的实现。

如果大量类正在实现此接口,则我们需要跟踪所有这些类并对其进行更改。这不仅繁琐,而且容易出错。

为了解决这个问题,Java引入了默认方法。默认方法像普通方法一样继承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
interface Polygon {
void getArea();

// default method
default void getSides() {
System.out.println("I can get sides of a polygon.");
}
}

// implements the interface
class Rectangle implements Polygon {
public void getArea() {
int length = 6;
int breadth = 5;
int area = length * breadth;
System.out.println("The area of the rectangle is " + area);
}

// overrides the getSides()
public void getSides() {
System.out.println("I have 4 sides.");
}
}

// implements the interface
class Square implements Polygon {
public void getArea() {
int length = 5;
int area = length * length;
System.out.println("The area of the square is " + area);
}
}

class Main {
public static void main(String[] args) {

// create an object of Rectangle
Rectangle r1 = new Rectangle();
r1.getArea();
r1.getSides();

// create an object of Square
Square s1 = new Square();
s1.getArea();
s1.getSides();
}
}

// 输出
// The area of the rectangle is 30
// I have 4 sides.
// The area of the square is 25
// I can get sides of a polygon.

在上面的示例中,我们创建了一个名为Polygon的接口。它具有默认方法getSides()和抽象方法getArea()

在这里,我们创建了两个实现PolygonRectangleSquare类。

Rectangle类提供getArea()方法的实现,并覆盖getSides()方法。但是,Square类仅提供getArea()方法的实现。

现在,在使用Rectangle对象调用getSides()方法时,将调用重写的方法。但是,对于Square对象,将调用默认方法。

接口中的私有和静态方法

Java 8还添加了另一个功能,在接口内部包含静态方法。

与类相似,我们可以使用其引用访问接口的静态方法。

1
2
3
4
5
6
7
// create an interface
interface Polygon {
staticMethod(){..}
}

// access static method
Polygon.staticMethod();

注意:在Java 9发行版中,接口中还支持私有方法。

我们无法创建接口的对象。因此,私有方法用作辅助方法,为接口中的其他方法提供支持。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// To use the sqrt function
import java.lang.Math;

interface Polygon {
void getArea();

// calculate the perimeter of a Polygon
default void getPerimeter(int... sides) {
int perimeter = 0;
for (int side: sides) {
perimeter += side;
}

System.out.println("Perimeter: " + perimeter);
}
}

class Triangle implements Polygon {
private int a, b, c;
private double s, area;

// initializing sides of a triangle
Triangle(int a, int b, int c) {
this.a = a;
this.b = b;
this.c = c;
s = 0;
}

// calculate the area of a triangle
public void getArea() {
s = (double) (a + b + c)/2;
area = Math.sqrt(s*(s-a)*(s-b)*(s-c));
System.out.println("Area: " + area);
}
}

class Main {
public static void main(String[] args) {
Triangle t1 = new Triangle(2, 3, 4);

// calls the method of the Triangle class
t1.getArea();

// calls the method of Polygon
t1.getPerimeter(2, 3, 4);
}
}

// 输出
// Area: 2.9047375096555625
// Perimeter: 9

在上面的程序中,我们创建了一个名为Polygon的接口。它包括一个默认方法getPerimeter()和一个抽象方法getArea()

我们可以用相同的方式计算所有多边形的周长,因此我们在Polygon中实现了getPerimeter()的主体。

现在,所有实现Polygon的多边形都可以使用getPerimeter()来计算周长。

但是,对于不同的多边形,计算面积的规则是不同的。因此,没有实现就包含getArea()

任何实现Polygon的类都必须提供getArea()的实现。

[多态][https://www.programiz.com/java-programming/polymorphism]

多态是面向对象编程的重要概念。它只是意味着不止一种形式。

也就是说,同一实体(方法,运算符或对象)可以在不同的场景中执行不同的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Polygon {

// method to render a shape
public void render() {
System.out.println("Rendering Polygon...");
}
}

class Square extends Polygon {

// renders Square
public void render() {
System.out.println("Rendering Square...");
}
}

class Circle extends Polygon {

// renders circle
public void render() {
System.out.println("Rendering Circle...");
}
}

class Main {
public static void main(String[] args) {

// create an object of Square
Square s1 = new Square();
s1.render();

// create an object of Circle
Circle c1 = new Circle();
c1.render();
}
}
// 输出
// Rendering Square...
// Rendering Circle...

在上面的示例中,我们创建了一个超类:Polygon和两个子类:SquareCircle。注意render()方法的使用。

render()方法的主要目的是渲染形状。但是,渲染Square的过程与渲染Circle的过程不同。

因此,render()方法在不同的类中的行为有所不同。或者,我们可以说render()是多态的。

为什么使用多态

多态允许我们创建一致的代码。在前面的示例中,我们还可以创建不同的方法:renderSquare()renderCircle()分别渲染SquareCircle

这将完美地工作。但是,对于每种形状,我们需要创建不同的方法。这将使我们的代码不一致。

为了解决这个问题,Java中的多态性使我们可以创建单个方法render(),该方法对于不同Shape的行为将有所不同。

注意:print() 方法也是多态的一个示例。它用于打印不同类型的值,例如char,int,string等。

Java中会在三个地方使用多态:

  1. 方法重写
  2. 方法重载
  3. 操作符重载

方法重写

在Java的继承过程中,如果父类和子类中都存在相同的方法。然后,子类中的方法将覆盖超类中的相同方法。这称为方法重写。

在这种情况下,相同的方法将在父类中执行一个操作,在子类中执行另一操作。例如,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Language {
public void displayInfo() {
System.out.println("Common English Language");
}
}

class Java extends Language {
@Override
public void displayInfo() {
System.out.println("Java Programming Language");
}
}

class Main {
public static void main(String[] args) {

// create an object of Java class
Java j1 = new Java();
j1.displayInfo();

// create an object of Language class
Language l1 = new Language();
l1.displayInfo();
}
}
// 输出
// Java Programming Language
// Common English Language

在上面的示例中,我们创建了一个名为Language的超类和一个名为Java的子类。在这里,方法displayInfo()LanguageJava中都存在。

displayInfo()的用途是打印信息。但是,它以LanguageJava打印不同的信息。

注意:调用的方法是在程序执行期间确定的。因此,方法覆盖是运行时多态。

方法重载

在Java类中,如果参数不同,则可以创建具有相同名称的方法。

1
2
3
4
void func() { ... }
void func(int a) { ... }
float func(double a) { ... }
float func(int a, float b) { ... }

这在Java中称为方法重载。在此,相同的方法将根据参数执行不同的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Pattern {

// method without parameter
public void display() {
for (int i = 0; i < 10; i++) {
System.out.print("*");
}
}

// method with single parameter
public void display(char symbol) {
for (int i = 0; i < 10; i++) {
System.out.print(symbol);
}
}
}

class Main {
public static void main(String[] args) {
Pattern d1 = new Pattern();

// call method without any argument
d1.display();
System.out.println("\n");

// call method with a single argument
d1.display('#');
}
}
// 输出
// **********

// ##########

在上面的示例中,我们创建了一个名为Pattern的类。该类包含一个名为display()的方法,该方法已重载。

1
2
3
4
5
// method with no arguments
display() {...}

// method with a single char type argument
display(char symbol) {...}

在这里,display()的主要功能是打印图案。但是,根据传递的参数,该方法将执行不同的操作:

  • 如果未传递任何参数,则输出*的模式
  • 如果传递了单个char类型参数,则输出参数的模式。

注意:调用的方法由编译器确定。因此,它也被称为编译时多态。

操作重载

Java中的某些运算符对不同的操作数的行为有所不同。

  • +操作符被重载以执行数字加法以及字符串连接
  • 运算符,如 |!!对于逻辑和按位运算是超载的。

+运算符用于添加两个实体。但是,在Java中,+运算符执行两个操作。

  1. +被用在两个数值之间时,它是数值相加用途.
1
2
3
4
5
int a = 5;
int b = 6;

// + with numbers
int sum = a + b; // Output = 11
  1. +用在两个字符串之间时,是连接两个字符串的用途.
1
2
3
4
5
String first = "Java ";
String second = "Programming";

// + with strings
name = first + second; // Output = Java Programming

注意:在类似C ++的语言中,我们可以定义运算符以对不同的操作数进行不同的处理。但是,Java不支持用户定义的运算符重载。

多态变量

如果变量在不同条件下引用不同的值,则该变量称为多态的。

对象变量(实例变量)表示Java中多态变量的行为。这是因为类的对象变量可以引用其类的对象及其子类的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class ProgrammingLanguage {
public void display() {
System.out.println("This is Programming Language.");
}
}

class Java extends ProgrammingLanguage {
@Override
public void display() {
System.out.println("This is Java.");
}
}

class Main {
public static void main(String[] args) {

// declare an object variable
ProgrammingLanguage pl;

// create object of Animal class
pl = new ProgrammingLanguage();
pl.display();

// create object of Java class
pl = new Java();
pl.display();
}
}
// 输出
This is Programming Language.
This is Java.

在上面的示例中,我们创建了ProgrammingLanguage类的对象变量pl。在此,pl是多态变量。

  • 在语句pl = new ProgrammingLanguage()中,pl引用ProgrammingLanguage类的对象。
  • 并且,在语句pl = new Java()中,pl引用Java类的对象。

封装

封装是面向对象编程的关键功能之一。封装是指将字段和方法绑定在单个类中。

它防止外部类访问和更改类的字段和方法。这也有助于实现数据隐藏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Area {

// fields to calculate area
int length;
int breadth;

// constructor to initialize values
Area(int length, int breadth) {
this.length = length;
this.breadth = breadth;
}

// method to calculate area
public void getArea() {
int area = length * breadth;
System.out.println("Area: " + area);
}
}

class Main {
public static void main(String[] args) {

// create object of Area
// pass value of length and breadth
Area rectangle = new Area(5, 6);
rectangle.getArea();
}
}
// 输出
// Area: 30

在上面的示例中,我们创建了一个名为Area的类。该类的主要目的是计算面积。

要计算面积,我们需要两个变量:lengthbreadth以及一个方法:getArea()。因此,我们将这些字段和方法捆绑在一个类中。

在这里,字段和方法也可以从其他类访问。因此,这不是数据隐藏。

这只是封装。我们只是将相似的代码放在一起。

注意:人们通常将封装视为数据隐藏,但这并非完全正确。

封装是指将相关领域和方法捆绑在一起。这可以用来实现数据隐藏。封装本身并不是隐藏数据。

为什么使用封装

  • 在Java中,封装可帮助我们将相关的字段和方法保持在一起,这使我们的代码更整洁且易于阅读。
  • 它有助于控制我们的数据字段的值。
1
2
3
4
5
6
7
8
9
class Person {
private int age;

public void setAge(int age) {
if (age >= 0) {
this.age = age;
}
}
}

在这里,我们将年龄变量设为私有,并在setAge()方法内部应用逻辑。现在,年龄不能为负。

  • gettersetter方法提供对我们的类字段的只读或只写访问。
1
2
getName()  // provides read-only access
setName() // provides write-only access
  • 它有助于解耦系统的组件。例如,我们可以将代码封装到多个包中。

这些解耦的组件(捆绑)可以独立并发地开发,测试和调试。并且,特定组件的任何更改都不会对其他组件产生任何影响。

  • 我们还可以使用封装来实现数据隐藏。在上面的示例中,如果我们将length和width变量更改为private,则对这些字段的访问将受到限制。

并且,它们对外部类保持隐藏。这称为数据隐藏。

数据隐藏

数据隐藏是一种通过隐藏实现细节来限制我们的数据成员访问的方法。封装还提供了一种隐藏数据的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Person {

// private field
private int age;

// getter method
public int getAge() {
return age;
}

// setter method
public void setAge(int age) {
this.age = age;
}
}

class Main {
public static void main(String[] args) {

// create an object of Person
Person p1 = new Person();

// change age using setter
p1.setAge(24);

// access age using getter
System.out.println("My age is " + p1.getAge());
}
}
// 输出
// My age is 24

在上面的示例中,我们有一个private字段age。由于它是private的,因此无法从类外部访问它。

为了访问age,我们使用了公共方法:getAge()setAge()。这些方法称为gettersetter方法。

age设为私有可以使我们未经授权的访问。这是数据隐藏。

嵌套类

Java中,您可以在另一个类中定义一个类。这样的类称为nested class

1
2
3
4
5
6
class OuterClass {
// ...
class NestedClass {
// ...
}
}

您可以使用Java创建两种类型的嵌套类。

  • 非静态嵌套类(内部类)
  • 静态嵌套类

非静态嵌套类(内部类)

非静态嵌套类是另一个类中的一个类。它有权访问封闭类(外部类)的成员。它通常被称为内部类。

由于内部类存在于外部类中,因此您必须首先实例化外部类,以便实例化内部类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class CPU {
double price;
// nested class
class Processor{

// members of nested class
double cores;
String manufacturer;

double getCache(){
return 4.3;
}
}

// nested protected class
protected class RAM{

// members of protected nested class
double memory;
String manufacturer;

double getClockSpeed(){
return 5.5;
}
}
}

public class Main {
public static void main(String[] args) {

// create object of Outer class CPU
CPU cpu = new CPU();

// create an object of inner class Processor using outer class
CPU.Processor processor = cpu.new Processor();

// create an object of inner class RAM using outer class CPU
CPU.RAM ram = cpu.new RAM();
System.out.println("Processor Cache = " + processor.getCache());
System.out.println("Ram Clock speed = " + ram.getClockSpeed());
}
}
// 输出
// Processor Cache = 4.3
// Ram Clock speed = 5.5

在上面的程序中,有两个嵌套类ProcessorRAM,它们嵌套在外部类CPU中.我们可以将内部类声明为受保护的.因此,我们已将RAM类声明为受保护的。

Main 类中

  • 我们首先创建一个名为cpu的外部类CPU的实例。
  • 然后使用外部类的实例,创建内部类的对象
1
2
3
CPU.Processor processor = cpu.new Processor;

CPU.RAM ram = cpu.new RAM();

注意: 我们使用点(.)运算符使用外部类创建内部类的实例。

在内部类中访问外部类的成员

通过使用this关键字访问外部类的成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class Car {
String carName;
String carType;

// assign values using constructor
public Car(String name, String type) {
this.carName = name;
this.carType = type;
}

// private method
private String getCarName() {
return this.carName;
}

// inner class
class Engine {
String engineType;
void setEngine() {

// Accessing the carType property of Car
if(Car.this.carType.equals("4WD")){

// Invoking method getCarName() of Car
if(Car.this.getCarName().equals("Crysler")) {
this.engineType = "Smaller";
} else {
this.engineType = "Bigger";
}

}else{
this.engineType = "Bigger";
}
}
String getEngineType(){
return this.engineType;
}
}
}

public class Main {
public static void main(String[] args) {

// create an object of the outer class Car
Car car1 = new Car("Mazda", "8WD");

// create an object of inner class using the outer class
Car.Engine engine = car1.new Engine();
engine.setEngine();
System.out.println("Engine Type for 8WD= " + engine.getEngineType());

Car car2 = new Car("Crysler", "4WD");
Car.Engine c2engine = car2.new Engine();
c2engine.setEngine();
System.out.println("Engine Type for 4WD = " + c2engine.getEngineType());
}
}
// 输出
// Engine Type for 8WD= Bigger
// Engine Type for 4WD = Smaller

在上面的程序中,我们在外部类Car中有一个名为`Engine的内部类。在这里,注意这行

1
if(Car.this.carType.equals("4WD")) {...}

我们正在使用this关键字来访问外部类的carType变量。您可能已经注意到,不是使用this.carType,而是使用Car.this.carType

这是因为如果我们没有提到外部类Car的名称,那么this关键字将代表内部类内部的成员。

同样,我们也从内部类访问外部类的方法。

1
if (Car.this.getCarName().equals("Crysler") {...}

重要的是要注意,尽管getCarName()是私有方法,但我们仍可以从内部类访问它。

静态嵌套

在Java中,我们还可以在另一个类中定义一个静态类。这种类称为静态嵌套类。静态嵌套类不称为静态内部类。

与内部类不同,静态嵌套类无法访问外部类的成员变量。这是因为静态嵌套类不需要您创建外部类的实例。

1
OuterClass.NestedClass obj = new OuterClass.NestedClass();

在这里,我们仅通过使用外部类的类名来创建静态嵌套类的对象。因此,外部类不能使用引用OuterClass.this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MotherBoard {

// static nested class
static class USB{
int usb2 = 2;
int usb3 = 1;
int getTotalPorts(){
return usb2 + usb3;
}
}

}
public class Main {
public static void main(String[] args) {

// create an object of the static nested class
// using the name of the outer class
MotherBoard.USB usb = new MotherBoard.USB();
System.out.println("Total Ports = " + usb.getTotalPorts());
}
}
// 输出
// Total Ports = 3

在上面的程序中,我们在类MotherBoard中创建了一个名为USB的静态类。

1
MotherBoard.USB usb = new MotherBoard.USB();

在这里,我们使用外部类的名称创建USB对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class MotherBoard {
String model;
public MotherBoard(String model) {
this.model = model;
}

// static nested class
static class USB{
int usb2 = 2;
int usb3 = 1;
int getTotalPorts(){
// accessing the variable model of the outer classs
if(MotherBoard.this.model.equals("MSI")) {
return 4;
}
else {
return usb2 + usb3;
}
}
}
}
public class Main {
public static void main(String[] args) {

// create an object of the static nested class
MotherBoard.USB usb = new MotherBoard.USB();
System.out.println("Total Ports = " + usb.getTotalPorts());
}
}
// 输出
// error: non-static variable this cannot be referenced from a static context

这是因为我们没有使用外部类的对象来创建内部类的对象。因此,没有引用存储在Motherboard.this中的外部类Motherboard

嵌套类关键点

  • Java将内部类视为类的常规成员。它们就像在类中声明的方法和变量一样。
  • 由于内部类是外部类的成员,因此您可以将任何访问修饰符(private,protected)应用到内部类,这在普通类无法实现的。
  • 由于嵌套类是其封闭的外部类的成员,您可以使用点.表示法来访问嵌套类及其成员。
  • 使用嵌套的类将使您的代码更具可读性,并提供更好的封装。
  • 非静态嵌套类(内部类)可以访问外部/封闭类的其他成员,即使它们被声明为私有的也是如此。

嵌套静态类

Java中一个类中可以包含另一个类,形成嵌套类.嵌套类有两种形式:

  • 嵌套非静态类
  • 嵌套静态类

我们使用关键字static使声明嵌套静态类。

注意:在Java中,仅允许嵌套类为静态。

与常规类一样,静态嵌套类可以包含静态和非静态字段和方法。

1
2
3
4
5
6
Class Animal {
static class Mammal {
// static and non-static members of Mammal
}
// members of Animal
}

静态嵌套类与外部类相关联。

要访问静态嵌套类,我们不需要外部类的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Animal {

// inner class
class Reptile {
public void displayInfo() {
System.out.println("I am a reptile.");
}
}

// static class
static class Mammal {
public void displayInfo() {
System.out.println("I am a mammal.");
}
}
}

class Main {
public static void main(String[] args) {
// object creation of the outer class
Animal animal = new Animal();

// object creation of the non-static class
Animal.Reptile reptile = animal.new Reptile();
reptile.displayInfo();

// object creation of the static nested class
Animal.Mammal mammal = new Animal.Mammal();
mammal.displayInfo();

}
}
// 输出
// I am a reptile.
// I am a mammal.

在上面的程序中,我们在Animal类中有两个嵌套的MammalReptile类。

创建非静态类Reptile的对象

1
Animal.Reptile reptile = animal.new Reptile()

要创建静态类Mammal的对象

1
Animal.Mammal mammal = new Animal.Mammal()

访问外层成员

静态嵌套类与外部类相关联。这就是为什么静态嵌套类只能访问外部类的类成员(静态字段和方法)的原因。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Animal {
static class Mammal {
public void displayInfo() {
System.out.println("I am a mammal.");
}
}

class Reptile {
public void displayInfo() {
System.out.println("I am a reptile.");
}
}

public void eat() {
System.out.println("I eat food.");
}
}

class Main {
public static void main(String[] args) {
Animal animal = new Animal();
Animal.Reptile reptile = animal.new Reptile();
reptile.displayInfo();

Animal.Mammal mammal = new Animal.Mammal();
mammal.displayInfo();
mammal.eat();
}
}

输出:

1
2
3
4
5
6
7
Main.java:28: error: cannot find symbol
mammal.eat();
^
symbol: method eat()
location: variable mammal of type Mammal
1 error
compiler exit status 1

在上面的示例中,我们在Animal类中创建了一个非静态方法eat()

现在,如果我们尝试使用对象mammal访问eat(),则编译器将显示错误。

这是因为mammal是静态类的对象,因此我们无法从静态类访问非静态方法。

静态顶级类

如上所述,只有嵌套类可以是静态的。我们不能有静态的顶级类。

1
2
3
4
5
6
7
8
9
10
11
static class Animal {
public static void displayInfo() {
System.out.println("I am an animal");
}
}

class Main {
public static void main(String[] args) {
Animal.displayInfo();
}
}

输出:

1
2
3
4
5
Main.java:1: error: modifier static not allowed here
static class Animal {
^
1 error
compiler exit status 1

在上面的示例中,我们尝试创建一个静态类Animal。由于Java不允许使用静态顶级类,因此会出现错误。

匿名类

在Java中,一个类可以包含另一个称为嵌套类的类。可以在不提供任何名称的情况下创建嵌套类。

没有任何名称的嵌套类称为匿名类。

必须在另一个类中定义一个匿名类。因此,它也被称为匿名内部类。

1
2
3
4
5
6
7
class outerClass {

// defining anonymous class
object1 = new Type(parameterList) {
// body of the anonymous class
};
}

匿名类通常扩展子类或实现接口。

  1. 匿名类扩展的超类
  2. 匿名类实现的接口

上面的代码在运行时创建了一个匿名类的对象object1.

注意:匿名类在表达式内定义。因此,在匿名类的末尾使用分号来表示表达式的末尾。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Polygon {
public void display() {
System.out.println("Inside the Polygon class");
}
}

class AnonymousDemo {
public void createClass() {

// creation of anonymous class extending class Polygon
Polygon p1 = new Polygon() {
public void display() {
System.out.println("Inside an anonymous class.");
}
};
p1.display();
}
}

class Main {
public static void main(String[] args) {
AnonymousDemo an = new AnonymousDemo();
an.createClass();
}
}
// 输出
// Inside an anonymous class.

在上面的示例中,我们创建了一个Polygon类。它具有单个方法display()

然后,我们创建了一个匿名类,该类扩展了Polygon类并覆盖了display()方法。

当我们运行程序时,将创建匿名类的对象p1。然后,对象将调用匿名类的display()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
interface Polygon {
public void display();
}

class AnonymousDemo {
public void createClass() {

// anonymous class implementing interface
Polygon p1 = new Polygon() {
public void display() {
System.out.println("Inside an anonymous class.");
}
};
p1.display();
}
}

class Main {
public static void main(String[] args) {
AnonymousDemo an = new AnonymousDemo();
an.createClass();
}
}
// 输出
// Inside an anonymous class.

在上面的示例中,我们创建了一个实现Polygon接口的匿名类。

匿名类的优势

在匿名类中,只要需要它们就创建对象。即创建对象以执行某些特定任务。

1
2
3
4
5
Object = new Example() {
public void display() {
System.out.println("Anonymous class overrides the method display().");
}
};

在这里,当我们需要重写display()方法时,将动态创建匿名类的对象。

匿名类也有助于我们使代码简洁。

单例模型

Singleton是一种设计模式,而不是Java特有的功能。它确保只创建一个类的实例。

这是我们如何在Java中使用单例

  • 创建一个private的构造函数,该构造函数限制在类之外创建对象

  • 创建一个引用单例对象的private属性

  • 创建一个public static方法,该方法允许我们创建和访问我们创建的对象。在方法内部,我们将创建一个条件来限制我们创建多个对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class SingletonExample {

// private field that refers to the object
private static SingletonExample singleObject;

private SingletonExample() {
// constructor of the SingletonExample class
}

public static SingletonExample getInstance() {
// write code that allows us to create only one object
// access the object as per our need
}
}
  • private static SingletonExample singleObject -对类对象的引用。
  • private SingletonExample() -私有构造函数,用于限制在类外部创建对象。
  • public static SingletonExample getInstance() - 此方法返回对该类唯一对象的引用。由于该方法是static的,因此可以使用类名进行访问。

单例使用

在处理数据库时可以使用单例。它们可用于创建连接池以访问数据库,同时为所有客户端重用相同的连接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Database {
private static Database dbObject;

private Database() {
}

public static Database getInstance() {

// create object if it's not already created
if(dbObject == null) {
dbObject = new Database();
}

// returns the singleton object
return dbObject;
}

public void getConnection() {
System.out.println("You are now connected to the database.");
}
}

class Main {
public static void main(String[] args) {
Database db1;

// refers to the only object of Database
db1= Database.getInstance();

db1.getConnection();
}
}
// 输出
// You are now connected to the database.
  • 我们创建了一个单例类Database
  • dbObject是一个类类型字段。这将引用类Database的对象
  • 私有构造函数Database()防止在类外部创建对象
  • 静态类类型方法getInstance()将类的实例返回到外界。
  • Main类中,有一个字段db1.我们调用getInstance()获取数据库实例传给字段db1.
  • 只能通过getConnection()方法获取Database唯一的实例
  • Database只有一个实例,所有客户端使用一个.

重要的是要注意,只有少数情况(如记录)使单例有意义。甚至数据库连接通常也不应是单例。

枚举

Java,枚举(枚举的缩写)是一种具有一组固定的常量值的类型。我们使用enum关键字声明枚举。

1
2
3
enum Size { 
SMALL, MEDIUM, LARGE, EXTRALARGE
}

在这里,我们创建了一个名为Size的枚举。它包含固定值SMALLMEDIUMLARGEEXTRALARGE

花括号内的这些值称为枚举常量(值)。

注意:枚举常量通常以大写形式表示。

1
2
3
4
5
6
7
8
9
10
enum Size {
SMALL, MEDIUM, LARGE, EXTRALARGE
}

class Main {
public static void main(String[] args) {
System.out.println(Size.SMALL);
System.out.println(Size.MEDIUM);
}
}

输出:

1
2
SMALL
MEDIUM

从上面的示例可以看出,我们使用枚举名称访问常量值。

同样,我们可以创建枚举类型的变量。

1
Size pizzaSize;

在这里,pizzaSizeSize类型的变量。它只能分配4个值。

1
2
3
4
pizzaSize = Size.SMALL;
pizzaSize = Size.MEDIUM;
pizzaSize = Size.LARGE;
pizzaSize = Size.EXTRALARGE;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
enum Size {
SMALL, MEDIUM, LARGE, EXTRALARGE
}

class Test {
Size pizzaSize;
public Test(Size pizzaSize) {
this.pizzaSize = pizzaSize;
}
public void orderPizza() {
switch(pizzaSize) {
case SMALL:
System.out.println("I ordered a small size pizza.");
break;
case MEDIUM:
System.out.println("I ordered a medium size pizza.");
break;
default:
System.out.println("I don't know which one to order.");
break;
}
}
}

class Main {
public static void main(String[] args) {
Test t1 = new Test(Size.MEDIUM);
t1.orderPizza();
}
}
// 输出
// I ordered a medium size pizza.

在上面的程序中,我们创建了一个枚举类型Size。然后,我们声明一个Size类型的变量pizzaSize

在这里,变量PizzaSize只能分配4个值(SMALLMEDIUMLARGEEXTRALARGE)。

1
Test t1 = new Test(Size.MEDIUM);

它将在Test类中调用Test()构造函数。现在,变量PizzaSize被分配了MEDIUM常数。

枚举类

在Java中,枚举类被认为是类的一种特殊类型。它是在Java 5发行版中引入的。

枚举类可以像常规类一样包含方法和字段。

1
2
3
4
5
enum Size {
constant1, constant2, …, constantN;

// methods and fields
}

当我们创建一个枚举类时,编译器将创建每个枚举常量的实例(对象)。同样,默认情况下,所有枚举常量始终是public static final

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
enum Size{
SMALL, MEDIUM, LARGE, EXTRALARGE;

public String getSize() {

// this will refer to the object SMALL
switch(this) {
case SMALL:
return "small";

case MEDIUM:
return "medium";

case LARGE:
return "large";

case EXTRALARGE:
return "extra large";

default:
return null;
}
}

public static void main(String[] args) {

// call getSize()
// using the object SMALL
System.out.println("The size of the pizza is " + Size.SMALL.getSize());
}
}
// 输出
// The size of the pizza is small

在上面的示例中,我们创建了一个枚举类Size。它具有四个常数:SMALLMEDIUMLARGEEXTRALARGE

由于Size是一个枚举类,因此编译器会自动为每个枚举常量创建实例。

main()方法中,我们使用实例SMALL调用getSize()方法。

枚举类中的方法

枚举类中有一些预定义的方法可以随时使用。

Java Enum ordinal()

ordinal()方法返回枚举常量的位置。

1
2
ordinal(SMALL) 
// returns 0

Enum compareTo()

compareTo()方法根据序数值比较枚举常量。

1
2
Size.SMALL.compareTo(Size.MEDIUM)
// returns ordinal(SMALL) - ordinal(MEDIUM)

Enum toString()

toString()方法返回枚举常量的字符串表示形式。

1
2
SMALL.toString()
// returns "SMALL"

Enum name()

name()方法以字符串形式返回枚举常量的定义名称。他从name()方法返回的值final

1
2
name(SMALL)
// returns "SMALL"

Java Enum valueOf()

valueOf()方法采用字符串,并返回具有相同字符串名称的枚举常量。

1
2
Size.valueOf("SMALL")
// returns constant SMALL.

Enum values()

values()方法返回一个包含所有枚举常量的枚举类型数组。

1
Size[] enumArray = Size.value();

为什么使用枚举

在Java中,引入了enum来替换int常量的使用。

使用了一个int常量集合。

1
2
3
4
5
6
class Size {
public final static int SMALL = 1;
public final static int MEDIUM = 2;
public final static int LARGE = 3;
public final static int EXTRALARGE = 4;
}

在这里,如果我们打印常数,就会出现问题。这是因为仅打印了数字,可能没有帮助。

因此,代替使用int常量,我们可以简单地使用枚举。

1
2
3
enum Size {
SMALL, MEDIUM, LARGE, EXTRALARGE
}

这使我们的代码更加直观。

同样,枚举提供了编译时类型的安全性。

如果我们声明一个Size类型的变量。

在此,可以确保该变量将保存四个值之一。现在,如果我们尝试传递这四个值以外的值,则编译器将生成错误。

枚举构造函数

在Java中,枚举类可能包含类似于常规类的构造函数。这些枚举构造器是

  • private - 类内可以访问
  • package-private - 包内可以访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
enum Size {

// enum constants calling the enum constructors
SMALL("The size is small."),
MEDIUM("The size is medium."),
LARGE("The size is large."),
EXTRALARGE("The size is extra large.");

private final String pizzaSize;

// private enum constructor
private Size(String pizzaSize) {
this.pizzaSize = pizzaSize;
}

public String getSize() {
return pizzaSize;
}
}

class Main {
public static void main(String[] args) {
Size size = Size.SMALL;
System.out.println(size.getSize());
}
}
// 输出
// The size is small.

在上面的示例中,我们创建了一个枚举Size。它包含一个private的枚举构造函数。构造函数将字符串值作为参数,并将值分配给变量pizzaSize

由于构造函数是private的,因此我们不能从类外部访问它。但是,我们可以使用枚举常量来调用构造函数。

Main类中,我们将SMALL分配给一个枚举变量size。然后,常量SMALL以字符串为参数调用构造函数Size

最后,我们使用size调用了getSize()

枚举字符串

在Java中,我们可以使用toString()方法或name()获得枚举常量的字符串表示形式

1
2
3
4
5
6
7
8
9
10
11
12
enum Size {
SMALL, MEDIUM, LARGE, EXTRALARGE
}

class Main {
public static void main(String[] args) {

System.out.println("string value of SMALL is " + Size.SMALL.toString());
System.out.println("string value of MEDIUM is " + Size.MEDIUM.name());

}
}

输出:

1
2
string value of SMALL is SMALL
string value of MEDIUM is MEDIUM

在上面的示例中,枚举常量的默认字符串表示形式是相同常量的名称。

修改默认枚举字符串值

我们可以通过重写toString()方法来更改枚举常量的默认字符串表示形式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
enum Size {
SMALL {

// overriding toString() for SMALL
public String toString() {
return "The size is small.";
}
},

MEDIUM {

// overriding toString() for MEDIUM
public String toString() {
return "The size is medium.";
}
};
}

class Main {
public static void main(String[] args) {
System.out.println(Size.MEDIUM.toString());
}
}
// 输出
// The size is medium

在上面的程序中,我们创建了一个枚举Size。并且我们已经覆盖了枚举常量SMALLMEDIUMtoString()方法。

注意:我们不能覆盖name()方法。这是因为name()方法是final的。

反射

在Java中,反射使我们能够在运行时检查和操作classinterfaceconstructormethodfield

Java类名Class

Java中有一个名为Class的类,该类在运行时保留有关对象和类的所有信息。

Class的对象描述特定类的属性,该对象用于执行反射。

创建Class类的对象

forName()

使用ClassforName()创建对象

forName()使用字符串参数(类的名称)并返回Class的对象。返回的对象引用由字符串指定的类。

1
2
Class Dog {  }
Class c1 = Class.forName("Dog");

getClass()

getClass()方法使用特定类的对象来创建Class的新对象。

1
2
Dog d1 = new Dog()
Class c1 = d1.getClass();

using.class

我们还可以使用.class扩展名创建Class对象。

1
Class c1 = Dog.class;

创建Class对象之后,我们可以使用这些对象进行反射。

获取接口

我们可以使用ClassgetInterfaces()方法来收集有关由该类实现的接口的信息。此方法返回接口数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import java.lang.Class;
import java.lang.reflect.*;

interface Animal {
public void display();
}

interface Mammal {
public void makeSound();
}

class Dog implements Animal, Mammal {
public void display() {
System.out.println("I am a dog.");
}

public void makeSound() {
System.out.println("Bark bark");
}
}

class ReflectionDemo {
public static void main(String[] args) {
try {
// create an object of Dog class
Dog d1 = new Dog();

// create an object of Class using getClass()
Class obj = d1.getClass();

// find the interfaces implemented by Dog
Class[] objInterface = obj.getInterfaces();
for(Class c : objInterface) {

// print the name of interfaces
System.out.println("Interface Name: " + c.getName());
}
}

catch(Exception e) {
e.printStackTrace();
}
}
}

输出

1
2
Interface Name: Animal
Interface Name: Mammal

获取超类和访问修饰符

类Class的方法getSuperclass()可用于获取有关特定类的父类的信息。

Class提供了一种getModifier()方法,该方法以整数形式返回class的修饰符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import java.lang.Class;
import java.lang.reflect.*;

interface Animal {
public void display();
}

public class Dog implements Animal {
public void display() {
System.out.println("I am a dog.");
}
}

class ReflectionDemo {
public static void main(String[] args) {
try {
// create an object of Dog class
Dog d1 = new Dog();

// create an object of Class using getClass()
Class obj = d1.getClass();

// Get the access modifier of Dog in integer form
int modifier = obj.getModifiers();
System.out.println("Modifier: " + Modifier.toString(modifier));

// Find the superclass of Dog
Class superClass = obj.getSuperclass();
System.out.println("Superclass: " + superClass.getName());
}

catch(Exception e) {
e.printStackTrace();
}
}
}

输出

1
2
Modifier: public
Superclass: Animal

反射字段,方法和构造函数

java.lang.reflect提供了可用于操纵类成员的类。

  • Method class - 提供有关类中方法的信息
  • Field class - 提供有关类中字段的信息
  • Constructor class - 提供有关类中的构造函数的信息

反射字段

可以使用Field类提供的各种方法来检查和修改类的不同字段。

getFields()

返回该类及其父类的所有公共字段

getDeclaredFields()

返回类的所有字段

getModifier()

以整数形式返回字段的修饰符

set(classObject, value)

给指定值的字段设定值

get(classObject)

得到一个字段的值

setAccessible(boolean)

使私有字段可访问

getField(“fieldName”)

从类返回名称为fieldName的公共字段。

getDeclaredField(“fieldName”)

从类返回名称为fieldName的字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.lang.Class;
import java.lang.reflect.*;

class Dog {
public String type;
}

class ReflectionDemo {
public static void main(String[] args) {
try{
Dog d1 = new Dog();
// create an object of the class Class
Class obj = d1.getClass();

// manipulating the public field type of Dog
Field field1 = obj.getField("type");
// set the value of field
field1.set(d1, "labrador");
// get the value of field by converting in String
String typeValue = (String)field1.get(d1);
System.out.println("type: " + typeValue);

// get the access modifier of type
int mod1 = field1.getModifiers();
String modifier1 = Modifier.toString(mod1);
System.out.println("Modifier: " + modifier1);
System.out.println(" ");
}
catch(Exception e) {
e.printStackTrace();
}
}
}

输出

1
2
type: labrador
Modifier: public
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.lang.Class;
import java.lang.reflect.*;

class Dog {
private String color;
}

class ReflectionDemo {
public static void main(String[] args) {
try {
Dog d1 = new Dog();
// create an object of the class Class
Class obj = d1.getClass();

// accessing the private field
Field field2 = obj.getDeclaredField("color");

// making the private field accessible
field2.setAccessible(true);
// set the value of color
field2.set(d1, "brown");
// get the value of type converting in String
String colorValue = (String)field2.get(d1);
System.out.println("color: " + colorValue);

// get the access modifier of color
int mod2 = field2.getModifiers();
String modifier2 = Modifier.toString(mod2);
System.out.println("modifier: " + modifier2);
}
catch(Exception e) {
e.printStackTrace();
}
}
}

输出

1
2
color: brown
modifier: private

反射中的方法

field一样,我们可以使用Method类提供的各种方法检查类的不同方法。

getMethods()

返回该类及其父类的所有公共方法.

getDeclaredMethod()

返回该类的所有方法

getName()

返回方法名称

getModifiers()

以整数形式返回方法的访问修饰符

getReturnType()

返回方法的返回类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.lang.Class;
import java.lang.reflect.*;

class Dog {
public void display() {
System.out.println("I am a dog.");
}

protected void eat() {
System.out.println("I eat dog food.");
}

private void makeSound() {
System.out.println("Bark Bark");
}

}

class ReflectionDemo {
public static void main(String[] args) {
try {
Dog d1 = new Dog();

// create an object of Class
Class obj = d1.getClass();

// get all the methods using the getDeclaredMethod()
Method[] methods = obj.getDeclaredMethods();

// get the name of methods
for(Method m : methods) {

System.out.println("Method Name: " + m.getName());

// get the access modifier of methods
int modifier = m.getModifiers();
System.out.println("Modifier: " + Modifier.toString(modifier));

// get the return types of method
System.out.println("Return Types: " + m.getReturnType());
System.out.println(" ");
}
}
catch(Exception e) {
e.printStackTrace();
}
}
}

输出

1
2
3
4
5
6
7
8
9
10
11
Method Name: display
Modifier: public
Return type: void

Method Name: eat
Modifier: protected
Return Type: void

Method Name: makeSound
Modifier: private
Return Type: void

反射中的构造函数

我们还可以使用Constructor类提供的各种方法来检查类的不同构造函数。

getConstructors()

返回一个类的所有公共构造函数以及该类的超类

getDeclaredConstructor()

返回所有构造函数

getName()

返回构造函数的名称

getModifiers()

以整数形式返回构造函数的访问修饰符

getParameterCount()

返回构造函数的参数数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import java.lang.Class;
import java.lang.reflect.*;

class Dog {

public Dog() {

}
public Dog(int age) {

}

private Dog(String sound, String type) {

}
}

class ReflectionDemo {
public static void main(String[] args) {
try {
Dog d1 = new Dog();
Class obj = d1.getClass();

// get all the constructors in a class using getDeclaredConstructor()
Constructor[] constructors = obj.getDeclaredConstructors();

for(Constructor c : constructors) {
// get names of constructors
System.out.println("Constructor Name: " + c.getName());

// get access modifier of constructors
int modifier = c.getModifiers();
System.out.println("Modifier: " + Modifier.toString(modifier));

// get the number of parameters in constructors
System.out.println("Parameters: " + c.getParameterCount());
}
}
catch(Exception e) {
e.printStackTrace();
}
}
}

输出

1
2
3
4
5
6
7
8
9
10
11
Constructor Name: Dog
Modifier: public
Parameters: 0

Constructor Name: Dog
Modifier: public
Parameters: 1

Constructor Name: Dog
Modifier: private
Parameters: 2