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关键字的作用
- 为对象分配内存空间。
- 引起对象构造方法的调用。
- 为对象返回一个引用。
使用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(); } }
输出结果为:
在匿名类内部。