Java对象、类与内部类、匿名类

原文:https://www.runoob.com/java/java-object-classes.html

Java作为一种面向对象语言。支持以下基本概念:

  • 多态
  • 继承
  • 封装
  • 抽象
  • 对象
  • 实例
  • 方法
  • 重载

本节我们重点研究对象和类的概念。

  • 对象:对象是类的一个实例(对象不是找个女朋友),有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
  • :类是一个模板,它描述一类对象的行为和状态。

下图中汽车类(class),而具体的每辆车为该汽车类的对象(object),对象包含了汽车的颜色、品牌、名称等。

 

 

一、Java中的对象

现在让我们深入了解什么是对象。看看周围真实的世界,会发现身边有很多对象,车,狗,人等等。所有这些对象都有自己的状态和行为。

拿一条狗来举例,它的状态有:名字、品种、颜色,行为有:叫、摇尾巴和跑。

对比现实对象和软件对象,它们之间十分相似。

软件对象也有状态和行为。软件对象的状态就是属性,行为通过方法体现。

在软件开发中,方法操作对象内部状态的改变,对象的相互调用也是通过方法来完成。

 

二、Java 中的类

1、类的定义和介绍

类可以看成是创建 Java 对象的模板。

通过上图创建一个简单的类来理解下 Java 中类的定义:

public class Dog {
    String breed;
    int size;
    String colour;
    int age;
 
    void eat() {
    }
 
    void run() {
    }
 
    void sleep(){
    }
 
    void name(){
    }
}

一个类可以包含以下类型变量:

  • 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
  • 成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
  • 类变量:类变量也声明在类中,方法体之外,但必须声明为 static 类型。

一个类可以拥有多个方法,在上面的例子中:eat()、run()、sleep() 和 name() 都是 Dog 类的方法。

变量类型:

1.局部变量:在方法、构造方法、语句块中定义的变量。其声明和初始化在方法中实现,在方法结束后自动销毁

public class  ClassName{
    public void printNumber(){
        int a;
    }
    // 其他代码
}

2.成员变量:定义在类中,方法体之外。变量在创建对象时实例化。成员变量可被类中的方法、构造方法以及特定类的语句块访问。

public class  ClassName{
    int a;
    public void printNumber(){
        // 其他代码
    }
}

3.类变量:定义在类中,方法体之外,但必须要有 static 来声明变量类型。静态成员属于整个类,可通过对象名或类名来调用。

public class  ClassName{
    static int a;
    public void printNumber(){
        // 其他代码
    }
}

 

2、成员变量和类变量的区别

由static修饰的变量称为静态变量,其实质上就是一个全局变量。如果某个内容是被所有对象所共享,那么该内容就应该用静态修饰;没有被静态修饰的内容,其实是属于对象的特殊描述。

不同的对象的实例变量将被分配不同的内存空间, 如果类中的成员变量有类变量,那么所有对象的这个类变量都分配给相同的一处内存,改变其中一个对象的这个类变量会影响其他对象的这个类变量,也就是说对象共享类变量。

成员变量和类变量的区别:

1、两个变量的生命周期不同

      成员变量随着对象的创建而存在,随着对象的回收而释放。

      静态变量随着类的加载而存在,随着类的消失而消失。

2、调用方式不同

      成员变量只能被对象调用。

      静态变量可以被对象调用,还可以被类名调用。

3、别名不同

      成员变量也称为实例变量。

      静态变量也称为类变量。

4、数据存储位置不同

      成员变量存储在堆内存的对象中,所以也叫对象的特有数据。

      静态变量数据存储在方法区(共享数据区)的静态区,所以也叫对象的共享数据。

 

static 关键字,是一个修饰符,用于修饰成员(成员变量和成员函数)。

特点:

1、想要实现对象中的共性数据的对象共享。可以将这个数据进行静态修饰。

2、被静态修饰的成员,可以直接被类名所调用。也就是说,静态的成员多了一种调用方式。类名.静态方式。

3、静态随着类的加载而加载。而且优先于对象存在。

 

弊端:

1、有些数据是对象特有的数据,是不可以被静态修饰的。因为那样的话,特有数据会变成对象的共享数据。这样对事物的描述就出了问题。所以,在定义静态时,必须要明确,这个数据是否是被对象所共享的。

2、静态方法只能访问静态成员,不可以访问非静态成员。

      因为静态方法加载时,优先于对象存在,所以没有办法访问对象中的成员。

3、静态方法中不能使用this,super关键字。

      因为this代表对象,而静态在时,有可能没有对象,所以this无法使用。

 

什么时候定义静态成员呢?或者说:定义成员时,到底需不需要被静态修饰呢?

成员分两种:

1、成员变量。(数据共享时静态化)

该成员变量的数据是否是所有对象都一样:

如果是,那么该变量需要被静态修饰,因为是共享的数据。 

如果不是,那么就说这是对象的特有数据,要存储到对象中。 

 

2、成员函数。(方法中没有调用特有数据时就定义成静态)

如果判断成员函数是否需要被静态修饰呢?

只要参考,该函数内是否访问了对象中的特有数据:

如果有访问特有数据,那方法不能被静态修饰。

如果没有访问过特有数据,那么这个方法需要被静态修饰。

 

成员变量和静态变量的区别:

1、成员变量所属于对象。所以也称为实例变量。静态变量所属于类。所以也称为类变量。

2、成员变量存在于堆内存中。静态变量存在于方法区中。

3、成员变量随着对象创建而存在。随着对象被回收而消失。静态变量随着类的加载而存在。随着类的消失而消失。

4、成员变量只能被对象所调用 。静态变量可以被对象调用,也可以被类名调用。所以,成员变量可以称为对象的特有数据,静态变量称为对象的共享数据。

 

3、成员变量和局部变量的区别

1.声明位置不同

成员变量也就是属性,在类中声明的。

局部变量,在方法中声明或代码块中声明。

 

2.初始值不同

成员变量如果没有赋值则是有默认值的,数据类型不同则默认值不同。

局部变量是没有默认值,也就是说必须先声明,再赋值,最后才使用。

 

3.在一个类中,局部变量可以与成员变量同名,但是局部变量优先,如果非要访问成员变量的属性,则必须使用 this.color

this 代表当前这个对象,也就是当前谁调用这个方法则这个对象就是谁。

 

三、构造方法

每个类都有构造方法。如果没有显式地为类定义构造方法,Java 编译器将会为该类提供一个默认构造方法。

在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,一个类可以有多个构造方法

如果有多个构造方法的时候,会根据实例化对象的入参去调用对应的构造方法。

类的构造方法

  • 构造方法的名字和类名相同,并且没有返回值。
  • 构造方法主要用于为类的对象定义初始化状态。
  • 我们不能直接调用构造方法,必须通过new关键字来自动调用,从而创建类的实例。
  • Java的类都要求有构造方法,如果没有定义构造方法,Java编译器会为我们提供一个缺省的构造方法,也就是不带参数的构造方法。

new关键字的作用

  1. 为对象分配内存空间。
  2. 引起对象构造方法的调用。
  3. 为对象返回一个引用。

使用java类实例化一个对象的时候,如果在类中不显式的声明其构造函数,则会使用一个默认的构造函数来初始化对象。

实例:

//一个没有显式声明构造函数的类
Public class People{
    int age = 23;
    Public void getAge(){
        System.out.print("the age is "+age);
    }
}

//用这个类来实例化一个对象
People xiaoMing = new People(); // People() 是People类的默认构造函数,它什么也不干
xiaoMing.getAge();//打印年龄

也可以在声明类的时候显式的声明一个构造函数:

//一个带显式构造函数的类
Public class People{
    int age = 23;
    Public void getAge(){
        System.out.print("the age is "+ age);
    }
    // 显式声明一个带参数的构造函数,用于初始化年龄
    Public People(int a){
        this.age = a; 
    }
}

//用这个类来实例化一个对象
People xiaoMing = new People(20); // 使用带参数的构造函数来实例化对象
xiaoMing.getAge(); // 打印出来的年龄变为20

 

一个类可以有多个构造方法:

public class Test {
    public Test() {
        System.out.println("这个构造方法没有入参");
    }

    public Test(int num) {
        System.out.println("这个构造方法有一个整型参数num: " + num);
    }

    public Test(String name) {
        System.out.println("这个构造方法有一个字符串参数name: " + name);
    }

    public static void main(String[] args) {
        Test obj1 = new Test();  // 调用第1个构造方法
        Test obj2 = new Test(1);  // 调用第2个构造方法
        Test obj3 = new Test("小明");  // 调用第3个构造方法
    }
}

 

四、创建对象

对象是根据类创建的。在Java中,使用关键字 new 来创建一个新的对象。创建对象需要以下三步:

  • 声明:声明一个对象,包括对象名称和对象类型。
  • 实例化:使用关键字 new 来创建一个对象。
  • 初始化:使用 new 创建对象时,会调用构造方法初始化对象。

下面是一个创建对象的例子:

public class Puppy{
   public Puppy(String name){
      //这个构造器仅有一个参数:name
      System.out.println("小狗的名字是 : " + name ); 
   }
   public static void main(String[] args){
      // 下面的语句将创建一个Puppy对象
      Puppy myPuppy = new Puppy("tommy");
   }
}

编译并运行上面的程序,会打印出下面的结果:

小狗的名字是 : tommy

 

五、访问实例变量和方法

通过已创建的对象来访问成员变量和成员方法,如下所示:

/* 实例化对象 */
Object referenceVariable = new Constructor();
/* 访问类中的变量 */
referenceVariable.variableName;
/* 访问类中的方法 */
referenceVariable.methodName();

 

下面的例子展示如何访问实例变量和调用成员方法:

public class Puppy {
    int puppyAge;

    public Puppy(String name) {
        // 这个构造器仅有一个参数:name
        System.out.println("小狗的名字是 : " + name);
    }

    public void setAge(int age) {
        puppyAge = age;
    }

    public int getAge() {
        System.out.println("小狗的年龄为 : " + puppyAge);
        return puppyAge;
    }

    public static void main(String[] args) {
        /* 创建对象 */
        Puppy myPuppy = new Puppy("tommy");
        /* 通过方法来设定age */
        myPuppy.setAge(2);
        /* 调用另一个方法获取age */
        myPuppy.getAge();
        /*你也可以像下面这样访问成员变量 */
        System.out.println("变量值 : " + myPuppy.puppyAge);
    }
}

编译并运行上面的程序,产生如下结果:

小狗的名字是 : tommy
小狗的年龄为 : 2
变量值 : 2

六、内部类

https://www.runoob.com/java/java-inner-class.html

内部类不是很好理解,但说白了其实也就是一个类中还包含着另外一个类。

如同一个人是由大脑、肢体、器官等身体结果组成,而内部类相当于其中的某个器官之一,例如心脏:它也有自己的属性和行为(血液、跳动)。

显然,此处不能单方面用属性或者方法表示一个心脏,而需要一个类。

而心脏又在人体当中,正如同是内部类在外部内当中。

 

Java 一个类中可以嵌套另外一个类,语法格式如下:

class OuterClass {   // 外部类
    // ...
    class NestedClass { // 嵌套类,或称为内部类
        // ...
    }
}

要访问内部类,可以通过创建外部类的对象,然后创建内部类的对象来实现。

嵌套类有两种类型:

  • 非静态内部类
  • 静态内部类

1、非静态内部类

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

由于内部类嵌套在外部类中,因此必须首先实例化外部类,然后创建内部类的对象来实现。

class OuterClass {
    int x = 10;

    class InnerClass {
        int y = 5;

        public InnerClass() {
            // 内部类访问外部类的成员变量:外部类.this.成员变量
            System.out.println(OuterClass.this.x);
        }
    }
}

public class MyMainClass {
    public static void main(String[] args) {
        OuterClass myOuter = new OuterClass();
        OuterClass.InnerClass myInner = myOuter.new InnerClass();
        System.out.println(myInner.y + myOuter.x);
    }
}

以上实例执行输出结果为:

10
15

 

2、私有的内部类

内部类可以使用 private 或 protected 来修饰,如果你不希望内部类被外部类访问可以使用 private 修饰符:

class Out {
    private int age = 12;

    private class In {
        public void print() {
            System.out.println(age);
        }
    }

    public void outPrint() {
        new In().print();
    }
}

public class Demo {
    public static void main(String[] args) {
        /* 此方法无效
         * Out.In in = new Out().new In();
         * in.print();
         */
        Out out = new Out();
        out.outPrint();
    }
}

运行结果:12

如果一个内部类只希望被外部类中的方法操作,那么可以使用 private 声明内部类。

上面的代码中,我们必须在 Out 类里面生成 In 类的对象进行操作,而无法再使用 Out.In in = new Out().new In() 生成内部类的对象。

也就是说,此时的内部类只有外部类可控制。

如同是,我的心脏只能由我的身体控制,其他人无法直接访问它。

 

3、方法内部类

class Out {
    private int age = 12;

    public void Print(final int x) {
        class In {
            public void inPrint() {
                System.out.println(x);
                System.out.println(age);
            }
        }
        new In().inPrint();
    }
}

public class Demo {
    public static void main(String[] args) {
        Out out = new Out();
        out.Print(3);
    }
}

运行结果:

3
12

在上面的代码中,我们将内部类移到了外部类的方法中,然后在外部类的方法中再生成一个内部类对象去调用内部类方法。

如果此时我们需要往外部类的方法中传入参数,那么外部类的方法形参必须使用 final 定义。

至于 final 在这里并没有特殊含义,只是一种表示形式而已。

 

4、静态内部类

静态内部类注意点:

  • 静态内部类中可以有非静态的方法。
  • 当内部类中有静态方法或者静态成员变量时,一定是静态内部类。

静态内部类可以使用 static 关键字定义,静态内部类我们不需要创建外部类来访问,可以直接访问它:

class OuterClass {
    int x = 10;
    private static int age = 12;

    static class InnerClass {
        int y = 5;

        public InnerClass() {
            // 静态内部类访无法问外部类的成员变量,因此注释这行(java: 无法从静态上下文中引用非静态 变量 this)
            // System.out.println(OuterClass.this.x);

            // 但是可以访问外部类的静态成员变量
            System.out.println(age);
        }

        public void getY() {
            // 即使是静态内部类的非静态方法也是无法访问外部类的成员变量
            // System.out.println(OuterClass.this.x);
            System.out.println(y);
        }
    }
}

public class MyMainClass {
    public static void main(String[] args) {
        OuterClass.InnerClass myInner = new OuterClass.InnerClass();
        myInner.getY();
    }
}

以上实例执行输出结果为:

12
5

注意:

可以看到,如果用 static 将内部类静态化,那么内部类就只能访问外部类的静态成员变量,具有局限性。

其次,因为内部类被静态化,因此 OuterClass.InnerClass 可以当做一个整体看,可以直接 new 出内部类的对象(通过类名访问 static,生不生成外部类对象都没关系)

 

5、内部类和外部类之间的相互调用

原文:https://www.cnblogs.com/jpfss/p/10993126.html

内部类的一般格式

public class Outer {
    class Inner{

    }
}

调用方式:

1.外部类访问内部类
内部类被static修饰:可以直接new
    Inner in = new Inner();

内部类没有被static修饰:先new外部类的实例,再new内部类实例
    Inner in = new Outer().new Inner();

2.内部类访问外部类
变量或者函数名不同的情况下
    直接使用:外部类变量或者函数

变量或者函数名相同的情况下
    外部类.this.变量或者函数

也就是说:

内部类在没有同名成员变量和局部变量的情况下,内部类会直接访问外部类的成员变量,而无需指定Out.this.属性名。

否则,内部类中的局部变量会覆盖外部类的成员变量

访问内部类本身的成员变量可用 this.属性名访问外部类的成员变量需要使用 Out.this.属性名。

 

内部类访问外部类:内部类可以直接访问外部类的成员包括私有成员,因为外部类持有内部类的引用

public class Outer {
    int x = 9;

    private class Inner {
        int x = 8;

        /*
         * 内部类访问外部类的成员变量和函数
         */
        private void test() {
            int x = 7;
            System.out.println("inner class method x: " + x);  // 内部类函数局部变量
            System.out.println("inner class x: " + this.x);  // 内部类成员变量
            System.out.println("outer class x: " + Outer.this.x);  // 外部类成员变量
            testing();  // 访问内部类的函数
            Outer.this.testing();  // 如果外部类函数名跟内部类函数同名:外部类.this.函数()
            testingOut();  // 如果外部类函数名跟内部类函数不是同名可以直接使用:函数()
        }

        private void testing() {
            System.out.println("test inner");
        }
    }

    private void testing() {
        System.out.println("test outer");
    }

    private void testingOut() {
        System.out.println("testingOut");
    }

    public static void main(String[] args) {
        Inner inner = new Outer().new Inner();
        inner.test();
    }
}

结果:

inner class method x: 7
inner class x: 8
outer class x: 9
test inner
test outer
testingOut

注意:内部类可以调用外部类private方法是因为实际他们都是在同一个类Outer中,因此可以调用

 

外部类访问内部类:必须建立内部类的对象

public class Outer {
    int x = 9;

    private class Inner {
        int x = 8;

        /*
         * 内部类访问外部类的成员变量
         */
        private void test() {
            int x = 7;
            System.out.println("inner class method x: " + x);  // 内部类函数局部变量
            System.out.println("inner class x: " + this.x);  // 内部类成员变量
            System.out.println("outer class x: " + Outer.this.x);  // 外部类成员变量
        }
    }

    private void testingOut() {
        System.out.println("外部类方法调用内部类方法");
        Inner inner = new Inner();
        inner.test();
    }

    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.testingOut();
    }
}

结果:

外部类方法调用内部类方法
inner class method x: 7
inner class x: 8
outer class x: 9

 

七、匿名类

https://www.runoob.com/java/java-anonymous-class.html

Java 中可以实现一个类中包含另外一个类,且不需要提供任何的类名直接实例化。

主要是用于在我们需要的时候创建一个对象来执行特定的任务,可以使代码更加简洁。

匿名类是不能有名字的类,它们不能被引用,只能在创建时用 new 语句来声明它们。

匿名类语法格式:

class outerClass {

    // 定义一个匿名类
    object1 = new Type(parameterList) {
         // 匿名类代码
    };
}

以上的代码创建了一个匿名类对象 object1,匿名类是表达式形式定义的,所以末尾以分号 ; 来结束。

匿名类通常继承一个父类或实现一个接口。

 

1、匿名类继承一个父类

以下实例中,创建了 Polygon 类,该类有一个方法 display(),AnonymousDemo 类继承了 Polygon 类并重写了 Polygon 类的 display() 方法:

class Polygon {
    public void display() {
        System.out.println("在 Polygon 类内部");
    }

    public void balala() {
        System.out.println("在 Polygon 类内部 balala");
    }
}

class AnonymousDemo {
    public void createClass() {
        // 创建的匿名类继承了 Polygon 类
        Polygon p1 = new Polygon() {
            public void display() {
                System.out.println("在匿名类内部。");
            }
        };
        p1.display();  // 调用的是重写后的方法
        p1.balala();  // 调用父类的方法
    }
}

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

执行以上代码,匿名类的对象 p1 会被创建,该对象会调用匿名类的 display() 方法,输出结果为:

在匿名类内部。
在 Polygon 类内部 balala

 

2、匿名类实现一个接口

以下实例创建的匿名类实现了 Polygon 接口:

interface Polygon {
    public void display();
}

class AnonymousDemo {
    public void createInterface() {
        // 匿名类实现一个接口
        Polygon p1 = new Polygon() {
            public void display() {
                System.out.println("在匿名类内部。");
            }
        };
        p1.display();
    }
}

class Main {
    public static void main(String[] args) {
        AnonymousDemo an = new AnonymousDemo();
        an.createInterface();
    }
}

输出结果为:

在匿名类内部。

 

posted @ 2022-08-29 22:23  我用python写Bug  阅读(78)  评论(0编辑  收藏  举报