您的位置:首页 > 职场人生

黑马程序员——面对对象———继承、多态、内部类等

2015-11-08 22:01 399 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

面对对象

一、面对对象

概念

面对对象是相对面向过程的而言,面向对象和面对过程都是一种编程的思想。

面对过程:强调的是功能行为代表:C语言;

例:把大象装进冰箱

1.      打开冰箱

2.      存储大象

3.      关上冰箱

看文字的字面信息,是以动作(功能)为主,我们要写方法对冰箱进行操作。

面对对象:将功能封装进对象,强调具备了功能的对象。代表:java、C++。

例:把大象装进冰箱

1.      冰箱打开

2.      冰箱存储

3.      冰箱关闭

看文字的字面信息,是以冰箱(对象)为主,我们把打开、存储、关闭这些方法封装进对象,我们直接调用对象的方法就可以了。

         特点:

         符合人们正常的思考习惯,可以将复杂的问题简单化。

         对象方法的封装,可以提高代码的复用性。

         面对对象开发,设计,特征

         开发过程:

         不断的创建需要的对象,使用对象,指挥对象完成任务。

         设计过程:

        管理和维护对象之间的关系。

         特征:

         封装(encapsulation)、继承(inheritance)、多态(polymorphism)

二、类和对象的关系

简单描述:

我们使用汽车和汽车的设计图纸来解释这个问题



我们有一个类(图纸),现在要根据这个类生成对象(汽车),那么JVM就相当于工厂

类的定义:

生活中描述事物无非就是描述事物的属性和行为。如:汽车有颜色、重量等属性,有发动、前进、刹车灯行为。

java中用类来描述事物:

         属性:类中的成员变量

         行为:类中的成员函数

         我们在设计类的时候,就是定义这些成员变量和成员函数。

成员变量和成员函数的区别?

成员变量:

1)  成员变量定义在类中,整个类都有访问权限

2)  成员变量随着对象的建立而建立,随着对象的消失而消失,存在于对象所在的堆内存中

3)  成员变量会自动初始化;

局部变量:

1)  局部变量定义在局部范围,如:函数内,代码块内等,随着{}的结束而结束。

2)  局部变量存在于栈内存中,作用的范围结束,变量空间会释放;

3)  局部变量不会自动初始化。

创建对象和使用

示例:

class  CarDemo
{
public static void main(String[] args)
{
//我们通过一个new来创建对象,使用Car c(类名 引用)来创建一个引用
Car c = new Car();
//使用“引用.函数”名来调用函数功能
c.show();
}
}
class Car
{
//定义在类中,为成员变量
String color = "red";
int length = 3;
//定义函数
void show(){
//定义在函数中,为局部变量
int length = 2;
System.out.println("车的颜色:"+color+"...车的长度:"+length);
}
}
result:



由结果看,当局部变量和成员变量冲突时,以局部变量为主。

对象的内存结构:

Car c1 = newCar();

c1.color ="blue";

Car c2 = newCar();



我们每用一次new关键字,就会在堆内存中开辟一个新空间,对象的引用指向该空间的首地址,我们改了其中一个对象的属性不会影响另一个。

匿名对象:

顾名思义就是没有名字(引用)的对象,一般情况下,对象就用一次的时候或者对象可以用作实际参数进行传递的时候使用。

基本数据类型参数及引用数据类型参数:

基本数据类型和引用数据类型的传递还是有些区别的,但是java中只有值传递。

示例:

class  CarDemo
{
public static void main(String[] args)
{
System.out.println("------匿名对象的使用-----");
//只使用一次
new Car().run();
System.out.println("------------");
//作为对象传递使用
show(new Car());
System.out.println("-----基本数据类型的传递-------");
//基本数据类型的传递
int x =3;
show1(x);
System.out.println("main方法中的x:"+x);
System.out.println("------引用数据类型的传递------");
//引用数据类型的传递
Car c = new Car();
show2(c);
System.out.println("main方法中的c.length:"+c.length);
}
//引用数据类型的传递
public static void show(Car c){
c.color = "black";
System.out.println("车的颜色"+c.color);
}
//基本数据类型的传递
public static void show1(int x){
x = 4;
System.out.println("show1方法中的x:"+x);
}
public
4000
static void show2(Car c){
c.length = 4;
System.out.println("show2方法改变过得c.length:"+c.length);
}
}
class Car
{

String color = "red";
int length = 3;
public void run(){
System.out.println("Car run");
}

}
result:



基本数据类型传递的执行过程:

1、jvm调用main方法,main方法入栈。

2、将x变量值设置为3。

3、main方法调用show方法,3作为基本数据类型参数赋值给show方法参数x,也就是说,此时show方法的参数x值为3。

4、show1方法执行x=4后,show1方法的参数x值变为4。

5、show1方法执行结束,show1方法出栈。show1方法参数x也随之出栈。

6、main方法打印x的值。此时x指的是main方法中的x变量的值(show方法中的参数x已经随show1方法一块出栈了)。所以,打印出来的x值为3而不是4。

7、main方法执行结束,出栈。

引用数据类型传递的执行过程:

1、jvm调用main方法,main方法入栈。

2、创建Car对象c(在堆内存中创建,c作为引用变量,指向堆内存中创建的实体对象),并将c指向的实体对象中的属性x的值设置为3。

3、main方法调用show2方法,c作为引用数据类型参数赋值给show2方法参数c,也就是说,此时show2方法的参数c和main方法中的变量c同时指向了堆内存中同一个实体对象。

4、show2方法执行c.x=4后,堆内存中的实体对象的x属性值变为4。

5、show2方法执行结束,show2方法出栈,show2方法参数c也随之出栈。虽然show2方法参数c出栈了,但是由于main方法的变量c依然引用着堆内存中的实体对象,因此堆内存中的实体对象不会被垃圾回收器清除。

6、main方法打印c.x的值。此时,c指的是main方法中的引用变量x,c.x指的依然是堆内存中的实体对象中c的值。所以,打印出来的值为4而不是9。

7、main方法执行结束,出栈。

封装:

隐藏对象的属性和实现细节,仅对外提供公共的访问方式。

好处:将代码的变化与外界隔离;2便于使用;3提高重用性;4提高安全性

封装原则:把不需要对外提供的内容隐藏起来,把只属于自己的属性隐藏起来,只提供getXXX,setXXX方法。

示例:

class Person
{
private int age;
public int getAge(){
return age;
}
public void setAge(int age){
if(age<=0){
System.out.println("数据错误!");
}else{
this.age = age;
}
}
public void eat(){
System.out.println("吃饭!!!");
}
}
public class Demo
{
public static void main(String [] args){
Person p = new Person();
p.setAge(20);
System.out.println(p.getAge());
}
}

我们对Person这个对象进行了封装,对person特有的age属性进行了描述。

构造函数:

1.      函数名和类名一致。

2.      不用定义返回值类型(是java语言中唯一不用定义返回值的函数)

3.      没有具体的返回值;

ps 加上返回值就变成了普通方法了。

          我们定义构造函数一般是给对象初始化使用的。

         示例:

package com.leaf.bean;
public class Person{
private int age;
private String name;
//构造函数
public Person(){
}
}

上述函数中就是在构造函数中对name和age进行了初始化。

P.S.

1、一般函数和构造函数什么区别呢?

构造函数:对象创建时,就会调用与之对应的构造函数,对对象进行初始化。

一般函数:对象创建后,需要函数功能时才调用。

构造函数:对象创建时,会调用并且只调用一次。

一般函数:对象创建后,可以被调用多次。

2、创建对象都必须要通过构造函数初始化。

一个类中如果没有定义过构造函数,那么该类中会有一个默认的空参数构造函数。

如果在类中定义了指定的构造函数,那么类中的默认构造函数就没有了。

3、多个构造函数是以重载的形式存在的。

示例

package com.leaf.bean;
public class Person{
private int age;
private String name;
//构造函数,只在对象创建的时候会执行一次,有且只有一次。
//空参数构造函数
public Person(){
System.out.println(“我是无参的构造函数”);
}
//使用重载的方式,传入两个参数
public Person(String name, int age){
System.out.println(“我是有参的构造函数”);
}
//普通方法,能重复使用
public void eat(){
System.out.println("吃饭");
}
}

构造代码块:

作用:给所有对象进行初始化

格式
{
System.out.println(“我是构造代码块”)
}

和构造函数同级,优先于构造函数执行

this关键字

this代表的是this关键字所在的{}所属的对象。即,this代表本类对象的引用。

当成员变量和局部变量有冲突的时候,可以用this来区分。

什么时候要使用this关键字?

当函数内需要用到调用函数的对象时,就用this。

示例:
package com.leaf.bean;
public class Person{
private int age;
private String name;
//构造函数,只在对象创建的时候会执行一次,有且只有一次。
//空参数构造函数
public Person(){}
//使用重载的方式,传入两个参数
public Person(String name, int age){
//这里使用了this,传入的String name和int age变量名和成员变量有冲突,我们可以使用this.age和this.name来代表成员变量,让成员变量等于局部变量。
this.age = age ;
this.name = name;
}
//普通方法,能重复使用
public void eat(){
System.out.println("吃饭");
}

}

this还能在其他的构造函数中定义,作用:调用其他的构造函数,但是只能定义在第一行。

static关键字:

         用于修饰成员(成员变量和成员函数)

          特点:

1.      随着类的加载而加载

2.      优先于对象存在

3.      能被所有对象共享

4.      调用方式:”类名.”直接调用

成员变量(实例变量)和静态变量(类变量)的区别:

1.      成员变量随着对象的创建而创建,对象被回收,成员变量释放;静态变量随着类的加载而存在,随着类的消失而消失。

2.      调用方式:

成员变量只能被对象调用

静态变量能被对象调用也能直接使用”类名.”来调用

3.      数据加载进内存的位置不同

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

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

         ps

1.      静态方法只能访问静态成员,因为静态方法加载进内存比非静态的方法早,不能让静态方法加载不存在的东西。

2.      静态方法不可以写this、super关键字,因为静态方法加载进内存的时候,还未创建对象,对象不存在当然无法加载。

示例1:

package com.leaf.bean;
public class Person1{
String food = "rice";
public static void eat1(){
System.out.println("吃饭"+food);
}
}
result:



示例2:

package com.leaf.bean;
public class Person1{
String food = "rice";
public static void eat1(){
String food1 = this.food;
}
}
result:



静态的作用:

1.静态变量:当分析对象中所具备的成员变量的值都是相同时,这时这个成员就可以被静态修饰。只要数据在对象中都是不同的,就是对象的特有数据,必须存储在对象中,是非静态的。如果是相同的数据,对象不需要做修改,只需要使用即可,不需要存储在对象中,定义成静态的。

2.静态函数:函数是否用静态修饰,就参考一点,就是该函数功能是否需要访问到对象中的特有数据。简单点说,从源代码看,该功能是否需要访问非静态的成员变量,如果需要,该功能就是非静态的。如果不需要,就可以将该功能定义成静态的。当然,也可以定义成非静态,但是非静态需要被对象调用。如果没有访问特有数据的方法,该对象的创建是没有意义。

静态代码块:随着类的加载而加载,只执行一次。作用:用于给类进行初始化。

package com.leaf.bean;
public class StaticCode{
static int num;
//静态代码块,给num进行初始化,赋值为10;
static{
num = 10;
}
}

三、继承

通过extends关键字让类和类之间产生继承关系。

多个类中存在相同属性和行为时,将这些内容抽取到单独的一个类中,那么多个类无需再定义这些属性和行为,只要使用extends来继承那个类就可以了。这段话中,多个类为子类,单独的类为父类(超类)。

ps:

子类可以访问父类中的非私有的属性和函数

子类无法继承父类中的私有属性和函数

示例:

class Person{
String name;
int age;
}
class Student extends Person
{
void study(){
System.out.println("学生:"+age);
}
}
class Worker extends Person
{
void work(){
System.out.println("工人:"+age);
}
}
public  class ExtendDemo
{
public static void main(String [] args){
Student s = new Student();
s.name = "lisi";
s.age = 20;
s.study();
System.out.println("-----------------");
Worker w = new Worker();
w.name = "zs";
w.age = 30;
w.work();
}
}
result:



继承的好处:

         继承的出现提高了代码的复用性

         继承的出现让类和类之间产生了关系,提供了多态的前提

继承的弊端:

         打破了代码的封装性,父类的代码对子类部分暴露了。

四、继承的特点

在java中只支持单继承,不支持多继承

原因:因为多继承容易出现问题。两个父类中有相同的方法,子类调用这个方法时,到底调用哪一个呢?java提供了另一种接口来代替多继承的实现,后续再谈。

多层继承体系:

C继承B,B继承A,就会出现继承体系。我们要了解一个继承体系,直接看最高的父类,逐步向下,就能快速的理解一个继承体系。

        

super关键字

1.      成员变量:

a)        this代表本类对象,那么super代表的是父类对象的引用,两者的用法相似

b)        本类成员用和局部变量同名用this来区别,父类和子类变量同名使用super来区分。

2.      成员函数

a)        当子类中有和父类中同样的成员函数时,不用super来调用,就会调用子类的函数,我们可以使用super.函数名来调用父类中的同名函数。这种情况叫做覆盖,子类和父类有同名同参的函数,子类覆盖父类。

b)        什么时候使用覆盖操作?当子类需要父类的功能,而功能主体子类有自己的一套方法时,就可以使用复写父类中的方法。

3.      构造函数

a)        子类在执行构造函数时,要先加载父类的构造函数,

b)        每一个子类的构造函数的第一句都会隐式的加上一句super();如果使用了super(param);来调用父类中特定的构造函数,默认的super()便不会加上,如果在第一句使用的this()或this(param)来调用子类中的构造函数,便会在调用的子类的构造函数中调用父类的构造函数,总之,子类构造函数执行必定会执行一个父类的构造函数(详细介绍在下一章节)。

示例:
class  SuperDemo
{
public static void main(String[] args)
{
System.out.println("----------------创建父类对象(无参)-----------------");
A a = new A();
System.out.println("----------------创建子类对象(无参)-----------------");
B b = new B();
System.out.println("----------------创建子类对象(有参)-----------------");
B b1 = new B(3);
System.out.println("----------------子类通过super调用父类的属性-----------------");
b.print();
System.out.println("----------------子类通过super调用父类的方法-----------------");
b.show1();
}
}
class A
{
String str = "我是父类的String";
A(){
System.out.println("父类的无参构造函数RUN");
}
A(int x){
System.out.println("父类的有参构造函数RUN,参数:"+x);
}
public void show(){
System.out.println("父类的一般函数RUN");
}
}
class B extends A
{
String str = "我是子类的String";
B(){
System.out.println("子类的无参构造函数RUN------");
}
B(int x){
super(4);
System.out.println("子类的有参构造函数RUN,参数:"+x+"------");
}
public void show(){
System.out.println("子类的一般函数RUN");
}
public void print(){
System.out.println("子类调用父类的属性:"+super.str);
}
public void show1(){
super.show();
}
}
result:



五、子类的实例化过程

子类中所有的构造函数默认都会访问父类中空参数的构造函数,因为每一个构造函数的第一行都有一句默认的super();

为什么子类要实例化要访问父类的构造函数?

因为子类是继承父类的类,不知道父类中的初始化(构造函数)对子类有没有影响,而子类要获取父类中的属性或函数也必须对父类进行初始化。

ps:

1.      当父类中没有空参数的构造函数时,子类要通过this或者super来指定要访问的构造函数

2.      子类的构造函数使用了this调用了自己的构造函数,那么默认的super语句就会消失,因为super和this都只能定义在第一行。无论如何定义super、this,子类的构造函数执行时,必须要有一句能访问父类构造函数的语句。

示例:

class Fu
{
String name ;
Fu(){
System.out.println("Fu无参构造函数");
}
Fu(String name){
System.out.println("Fu有参构造函数");
this.name = name ;
}
}
class Zi extends Fu
{
String name;
Zi(){}
Zi(String name){
super("lisi");
this.name = name;
}
}
class NewInstanceDemo
{
public static void main(String[] args){
Zi z = new Zi();
System.out.println("-----------------");
Zi z1 = new Zi("张三");
}
}


result:



一个对象实例化过程,以Zi z = newZi ();为例:

1.JVM会读取指定的路径下的Zi.class文件,并加载进内存,并会先加载Zi的父类Fu(如果有直接的父类的情况下)。

2.在内存中开辟空间,并分配地址。

3.并在对象空间中,对对象的属性进行默认初始化。

4.调用对应的构造函数进行初始化。

5.在构造函数中,第一行会先到调用父类中构造函数进行初始化。

6.父类初始化完毕后,再对子类的属性进行显示初始化。

7.再进行子类构造函数的特定初始化。

8.初始化完毕后,将地址值赋值给引用变量。

六、final关键字

final可以修饰类,方法、变量;final修饰的类不可以被继承;final修饰的方法不可以被覆盖;final修饰的变量时一个常量,只能被赋值一次。

final的使用:如果程序中有一个变量是固定的,我们会定义这个变量,但是别人看代码的时候却不会一眼就看出这个变量时固定的,这时我们就要对这个变量使用final。

七、抽象类

概述:

抽象定义:

抽象就是从多个事物中将共性的、本质的内容抽取出来。猫和狗都是动物,所以就可以抽象出一个类为动物。

抽象类:

Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。

抽象方法的由来:

多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的方法称为抽象方法。

比如:猫和狗都有吃饭的方法,但是猫喜欢吃鱼,狗喜欢吃骨头,吃的东西都不一样,但是都有吃的方法,就可以在动物类中定义一个吃饭的方法。

那么抽象类如何定义呢?

抽象类和抽象方法必须用abstract关键字来修饰。抽象方法只有方法声明,没有方法体,定义在抽象类中。

格式:

修饰符abstract 返回值类型 函数名(参数列表);

抽象类不可以被实例化,也就是不可以用new创建对象。

原因如下:

1.抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。例如:动物是一个抽象的概念,真正存在的是猫和狗。

2.而且抽象类即使创建了对象,调用抽象方法也没有意义。

3.抽象类通过其子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后才可以创建对象,否则该子类也是抽象类。

示例:

class AbstractDemo
{
public static void main(String[] args)
{
Cat c = new Cat();
c.eat();
System.out.println("-------------------");
Dog d = new Dog();
d.eat();
}
}
abstract class Animal
{
abstract void eat();
}
class Cat extends Animal
{
void eat(){
System.out.println("吃鱼");
}
}
class Dog extends Animal
{
void eat(){
System.out.println("吃骨头");
}
}
result:



抽象类详解:

抽象类是否拥有构造方法?有,用于给子类对象进行初始化。

抽象关键字abstract不可以和哪些关键字共用?private、static、final。

抽象类可以没有抽象方法吗?可以。抽象方法能有非抽象方法吗?可以

八、接口

当一个抽象类中的所有方法都是抽象方法的时候,就可以使用接口来定义这个类。

格式:interface {}

 

接口中的成员修饰符是固定的:

1.      成员常量:public static final

2.      成员函数:public abstract

接口是对外暴露的规则,设计程序功能的扩展

ps:

1.      虽然抽象类中的全局变量和抽象方法的修饰符可以不用写,但是这样比较难看懂,还是写上比较好。

2.      类与类的继承关系,接口和类之间是实现关系。

3.      接口不可以被实例化,能由实现了接口并覆盖了接口中所有抽象方法的子类实例化,否者这个子类就是一个抽象类。

示例:

class  InterfaceDemo
{
public static void main(String[] args)
{
DemoImpl d = new DemoImpl();
System.out.println(d.NUM);
System.out.println(DemoImpl.NUM);
System.out.println(Demo.NUM);
}
}
interface Demo
{
public static final int NUM = 4;
public abstract void show1();
public abstract void show2();

}
class DemoImpl implements Demo
{
public void show1(){}
public void show2(){}
}
result:



接口中出现将“多继承”通过另一种形式体现出来,即“多实现”。

在java中不直接支持多继承,因为会出现调用的不确定性。所以,java将多继承机制进行了改良,在java中变成了多实现,一个类可以实现多个接口。接口的出现避免了单继承的局限性。

interface A{
public void show();
}
interface B{
public void show();
}
class C{
public void method(){
…
}
}
//多实现
class Test extends C implements A,B{
public void show(){
System.out.println(“TEST”);
}
}
class InterfaceDemo{
public static void main(String[] args){
Test t = new Test();
t.show();
}
}

这样一个类在继承一个类的时候还能实现多接口

接口和抽象类:

         相同点:

都是不断的向上抽取而来的。

         不同点:

1.      抽象类需要被继承,而且只能进行单继承。接口需要被实现,但是可以多实现。

2.      抽象类中可以定义抽象方法和非抽象方法,子类继承后,可以直接使用非抽象方法;接口只能定义抽象方法,必须由子类去实现。

3.      抽象类的继承是is a的关系,定义该体系的基本共性内容。接口的实现是like a关系。

九、多态

定义:某一类食物的多种存在形态。

例如:动物中---猫、狗

我们一般情况下是这样想的:猫c = new 猫();

多态的思想就是,猫是动物的一员,所以:动物a = new 猫();

动物是猫和狗具体事物抽取出来的父类。用父类的引用指向子类的对象。

体现:父类或者接口的引用指向或接受自己的子类对象。

作用:多态的存在提高了程序的扩展性和后期的可维护性。,

前提:

1.需要存在继承或者实现的关系

2.需要有覆盖操作

好处:提高了代码的扩展性,前期的定义的代码,可以通过多态的特性使用后期定义的功能内容。

弊端:前期定义的内容不能使用(调用)后期子类的特有内容。

多态时成员的特点:

1.      成员变量

编译时:参考引用型变量所属的类中是否有调用的成员变量。有,编译通过,没有,编译失败。

运行时:参考引用型变量所属的类中是否有调用的成员变量,并运行该所属类中的成员变量。

简单说:编译和运行都参考等号的左边。

2.      成员函数(非静态)

编译时:参考引用型变量所属的类中是否有调用的函数。有,编译通过。没有,编译失败。

运行时:参考的是对象所属的类中是否有调用的函数。

简单说:编译看左边,运行看右边。

3.      静态函数

编译时:参考的是对象所属的类中是否有调用的函数。

运行时:参考的是对象所属的类中是否有调用的函数。

简单说:编译和运行看左边。

 

示例:

class DuotaiDemo
{
public static void main(String[] args)
{
Cat c = new Cat();
Dog d = new Dog();
method(c);
method(d);
System.out.println("-------------------");
//自动类型提升,买对象提升到了动物类型,但是便无法访问猫的特有功能了
Animal a = new Cat();
//子类和父类都有相同名字的方法的时候,使用子类的方法
a.eat();
//如果你想用猫的特有功能,就必须强转的,向下转型
System.out.println("---------向下转型----------");
Cat c1 = (Cat)a;
c.catchMouse();
System.out.println("-------------------");
Animal d1 = new Dog();
//不可以调用子类有父类没有的方法
//d1.look();
d1.staticMethod();
}
public static void method(Animal a ){
a.eat();
}
}
abstract class Animal
{
abstract void eat();
static void staticMethod(){
System.out.println("Animal static method");
}
}
class Cat extends Animal
{
void eat(){
System.out.println("吃鱼");
}
void catchMouse(){
System.out.println("抓老鼠");
}
static void staticMethod(){
System.out.println("cat static method");
}
}
class Dog extends Animal
{
void eat(){
System.out.println("吃骨头");
}
void look(){
System.out.println("看家");
}
}
result:



十、内部类

定义:将一个类定义在另一个类的内部,就叫内部类。

访问的权限:

内部类可以直接访问外部类中的成员,包括私有的成员,而外部类要访问内部类的成员必须建立内部类的对象。

内部类的设计:

分析事物时,发现该事物描述中还有事物,而且这个事物还在访问被描述事物的内容,这时候就定义内部类。

示例:
class InnerClassDemo
{
public static void main(String[] args)
{
//通过外部类调用内部类
Outer out = new Outer();
out.method();
System.out.println("--------------");
//直接访问外部类的内部成员
Outer.Inner in = new Outer().new Inner();
in.show();
}
}
class Outer
{
private int num = 3;
class Inner //内部类
{
void show(){
System.out.println("Show Num:"+num);
}
}
public void method(){
Inner in = new Inner();
in.show();
}
}
result:



内部类定义在成员位置上,所以内部类能被private、static的成员修饰符所修饰。被static修饰的内部类只能访问外部类中的静态成员。

示例:

修改上述内部类

class InnerClassDemo
{
public static void main(String[] args)
{
//通过外部类调用内部类
Outer out = new Outer();
out.method();
System.out.println("--------------");
//直接访问外部类的内部成员
Outer.Inner in = new Outer().new Inner();
in.show();
System.out.println("--------------");
//静态内部类的访问
Outer1.Inner1 in1 = new Outer1.Inner1();
in1.show();
}
}
class Outer
{
int num = 3;
int i = 4;
class Inner //内部类
{
void show(){
System.out.println("Show Num:"+num);
//System.out.println("Show I:"+ i);
}
}
public void method(){
Inner in = new Inner();
in.show();
}
}
class Outer1
{
static int num = 4;
static class Inner1 //静态内部类
{
void show(){
System.out.println("Show Num:"+num);
}
}
}
result:



如果内部类定义了静态成员,内部类也必须是静态的。

分析:为什么内部类可以调用外部类的成员呢?

          因为内部类持有外部类的引用,“外部类名.this”

内部类定义在局部位置上,也可以直接访问外部类中的成员。同时还可以访问所在局部中的局部变量,但必须是被final修饰的。

class Outer{
int num = 3;
void method(final int y){
final int x = 9;
class Inner{
void show(){
System.out.println("show..." + x + "," + y);
}
}
Inner in = new Inner();
in.show();
}
}
class InnerClassDemo1{
public static void main(String[] args){
new Outer().method(4);
}
}
result:



匿名内部类:

定义:内部类的简化写法

前提:内部类可以继承或实现一个外部类或接口

格式

new 外部类名或者接口名(){覆盖类或者接口中的代码,(也可以自定义内容)}

简单理解

就是建立一个带内容的外部类或者接口的子类匿名对象

什么时候使用匿名内部类?

通常使用的方法是接口类型的参数,并且该接口的方法不超过3个,可以将匿名内部类作为参数传递。

好处:增强阅读性。

示例:

interface Inter{
void show1();
void show2();
}

class Outer{
public void method(){
Inter in = new Inter(){
public void show1(){
System.out.println("...show1...." );
}
public void show2(){
System.out.println("...show2...." );
}
};
in.show1();
in.show2();
}
}

class InnerClassDemo2{
public static void main(String[] args){
new Outer().method();
}
}
result:



对象的初始化过程:

class Fu{
int num = 9;
{
System.out.println("Fu" );
}
Fu(){
super();//Object
//显示初始化
//构造代码块初始化
show();
}
void show(){
System.out.println("fu show " + num);//被覆盖,运行子类的
}
}

class Zi extends Fu{
int num = 8;
{
System.out.println("Zi" );
}
Zi(){
super();
//显示初始化
//构造代码块初始化
show();
}

void show(){
System.out.println("zi show " + num);
}
}

public class Demo{
public static void main(String[] args){
new Zi();
}
}
result:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息