java面向对象详解

Java语言是一种面向对象的高级语言程序,应用面向对象语言的求解基本思路是:首先分析问题并建立相应的对象,然后通过这些对象以及它们之间的配合解决问题,其中每个对象在计算机中占用一定的内存,同时能够完成一定功能。

类、域、方法和实例对象

类(class):是一组行为属性的集合。是实例对象的模板,类的定义格式是:

[类修饰词列表] class 类名 [extends 父类名] [implements 接口名称列表]
{
     类体
}

类的声明:

  1. 修饰词列表:可选项。如果存在多个类修饰词,则在相邻两个类修饰词之间采用空格分隔开。类修饰词用来说明类的属性,包括public、abstract、final、和strictfp等。

default:默认值,定义只能在本包中使用。
public:定义类可以被java的所有软件包使用,否则定义类只能在当前软件包中使用。
abstract:定义类是一个抽象类。
final:定义类不能用作父类。不能被覆盖(不应用于动态查询)。
strictfp:定义类中各个复电数的表示及运算严格遵循IEEE754算术国际标准。
static:成名为static的内部类是一个顶级类,它和包含类的成员是不相关的。

  1. 类名:类名可以是任意的合法标识符,若类的修饰词是public,则该类应当与所在文件名相同。在同一个Java源文件中可以包含多个类,但不能包含两个及以上具有public修饰词的类。
  2. extends 父类名:指定所定义类的父类,所定义类将具有其父类所定义的一些属性和功能。在定义类时如果不含有选项“extends 父类名”,则上面定义的类的父类是“java.lang.Object”,即不含选项“extends 父类名”与包含选项“extends java.lang.Object”具有相同的功能。类java.lang.Object是除了其自身外的所有类的直接或间接父类。
  3. implements 接口名称列表:具有该属性,表明定义类是实现了这些给定接口的类,即定义类将具有这些给定接口的属性和功能。当包含多个接口时,接口之间采用逗号分隔。

类体

在类体部分可以定义类的构造方法和类的两类成员要素:成员域(field)和成员方法(method)

成员域

类的成员域简称为域,通常用来表示和存储类所需要的数据,其格式为:

[域修饰词列表]  类型 变量名或带初始化的变量名列表;

1、域修饰词列表:可选项,可以0个或多个,若存在多个,相邻两个域修饰词之间采用空格分隔开。域修饰词通常包括public、protected、private、static、final、transient和volatile。修饰词public、protected和private不能同时存在,它们表示当前定义成员域的访问控制属性,即当前定义的成员域的应用范围。
static:表明当前定义的成员域是静态的。
final:要求立即对当前的成员域赋值(必须进行初始化变量),且赋值之后不能再修改该域的值。
transient:表明当前成员域是一种暂时的成员域,即当进行对象保存时可以不必保存当前的成员域。
volatile:主要用在多线程程序设计中,表明在访问当前成员域时将采用同步机制。
2、类型:指定当前成员域的类型。可以是基本数据类型,如 int,也可以是引用数据类型,如 类名。
3、变量名或带初始化的变量名列表:可以包含1个或多个变量名,每个变量名是一个合法的标识符,若含有多个变量名,则相邻变量名或带初始化的变量名之间采用逗号分隔开。包含多个变量名时,实际定义了多个成员域,即每个变量名对应一个成员域。带初始化的变量名实际是包含赋值运算,如:int m_radius=0;

成员方法

类的成员方法简称为方法,通常用来实现类的各种功能,其格式为:

[方法修饰词列表] 返回类型 方法名(方法的参数列表)
{
	方法体
}

1、方法修饰词列表:可选项。通常包括public、protected、private、abstract、static、final、synchronized和strictfp。方法修饰词public、protected和private不能同时存在,它们表示当前定义的成员方法的访问控制属性,即当前成员方法的封装性。
abstract:表明当前成员方法是抽象成员方法。抽象成员方法不能包含有方法体。
static:表明当前定义的成员方法是静态的。
final:当前成员方法所在类的子类中不能出现与当前成员方法相同的声明。
synchronized:表明当前成员方法是一种同步成员方法。
strictfp:表明在当前成员方法中各个浮点数的表示及其运算严格遵循IEEE算术国际标准。
2、返回类型:指定当前成员方法返回的数据的数据类型。可以是基本数据类型,如:int;也可以是引用数据类型,如:类型。若成员方法不返回任何数据,则应当在返回类型处写上关键字 void,否则编译将出错。
3、方法名:一个合法的标识符,用来表示当前的成员方法。
4、参数列表:可包含0个或多个,在参数列表处除了空格之外,不含任何字符时,表明该参数列表不含任何参数。需注意,不能再参数列表处写上关键字void,否则编译将出错。在参数列表中包含多个参数时,参数之间采用逗号分隔开。每个参数的格式是:

类型 参数变量名
方法体

通常由一些语句组成,主要用来实现当前成员方法的功能。

类的构造方法

主要用来创建类的实例对象,通常同时完成新创建的实例对象的初始化工作。
定义构造方法的格式:

[构造方法修饰词列表]类名(方法的参数列表)
{
  	方法体
}

构造方法具有以下三个基本特点:

  1. 构造方法名必须与类名相同
  2. 构造方法不具有任何返回类型
  3. 任何一个类都含有构造方法
  4. 构造方法可以用publicprivateprotected修饰

创建类的实例对象可以通过new运算符和类的构造方法
其格式为:

new 构造方法名(构造方法调用参数列表)

通过该变量访问该实例对象的成员域的格式为:

变量名.成员域名
变量名.成员方法名(成员方法调用参数列表)

构造方法重载

与方法重载类似。

public class Test {
    int id;
    String name;
    int age;
    Test(int i) {
        this.id = id;
    }
    Test(String name, int a) {
        this.name = name;// this区分成员变量和局部变量
        this.age = a;
    }
    Test(int i, String n, int a) {
        this.id=i;
        this.name=n;
        this.age = a;
    }
}

类的实例对象的生命周期包括实例对象的创建、实例对象的使用、实例对象的废弃以及垃圾的回收。

Java的垃圾回收机制(对象的内存回收)

说明:对于任何一个实例对象,如果没有任何引用指向该实例对象,则该实例对象所占据的内存是不再被java程序所用的内存,即为”垃圾“。

//创建类integer的一个实例对象,变量a指向该实例对象
integer a = new integer(11)
a=null

a=null使a不在指向该实例对象 ,这时该实例对象所占的内存为”垃圾“。java虚拟机一般不会立即回收该垃圾,需要等待合适的时机。java系统自定义了垃圾回收算法,用了提高垃圾回收效率。因此java系统并不保证先申请存储单元会先被释放,也不保证先成为垃圾的存储单元会先被释放。在java系统提供的类System中含有成员方法:

public static void gc()

调用该方法可以向java虚拟机申请尽快进行垃圾回收,但不能保证立即回收。因为这个成员方法是静态方法,所以一般通过下面方式调用

System.gc()

继承性

继承的概念

继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

类的继承格式

[类修饰列表] class 类名 [extends 父类名] [implements 接口名称列表]
{
	类体
}

比如一个简单的例子:也可以定义一个接口传送门

class 父类 {
}
class 子类 extends 父类 {
}

注意事项!!
1、当一个类没有继承任何一个类时,系统默认继承Object。
2、父类又被称为基类、超类、super类,子类又被称为派生类。
3、Java的继承是单一性的。
4、子类不能继承父类的构造方法,但是可以继承构造方法类的参数。
5、子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。但子类不能继承父类private修饰的属性和方法。(下面说修饰符)

对象的类型转换

(口诀:自动向上转型,强制向下转型)看完类型转换后在看这个!
下面给出一个简单的继承关系代码:

class Father {
}
public class Son extends Father {
}
子类型和父类型之间的类型转换

1.隐式类型转换:将子类型的数据转化成父类型的数据。

Son son = new Son();
Father a = son;

上述代码可以看到,将类型为子类Son的变量son直接赋值给类型为父类的Father的变量a。当然也可使用强制类型转换运算符(),比如:

(Father)son

2.显示类型转换:将类型为父类型的数据转换成子类型的数据。这里通常要使用强制类型转换运算符()
通过隐式转换我们可以是否可以推导出显示类型转换呢?将父类型的数据转换成子类型的数据是否可以这么写呢?

Father a = new Father();
Son b = (Son)a;

注意:这种写法编译时不会出现错误,但是运行时会出现类型转换错误,因为无法从变量a所指向的实例对象中得到其子类Son的实例对象。
正确的应该是:

Son son = new Son();
Father a = son;
Son b = (Son)a;

在上面的语句中,将类型为父类Father的变量a通过强制类型转换运算符转换成子类Son的类型,再赋值给类型为子类Son的变量b。
提醒:如果两个类型不存在子类型与父类型之间的关系,则一般不能进行类型转换,例子如下:

Father a = new Father();
(String)a
子类实例对象和父类实例对象的关系

我们可以认为子类的实例对象同时也是父类的实例对象;但是反过来,父类的构造方法创建的实例对象一般不是子类的实例对象。判断两种实例对象关系可以通过instanceof运算符来实现,格式为:

引用类型表达式 instanceof 引用类型

运算结果返回一个布尔值。当引用类型表达式不是null并且所指向的实例对象是指定引用类型的实例对象时,返回true;否则返回false。比如:

Son a = new Son();
Father b = new Father();
Father c = a;

则下面的表达式结果为:

a instanceof Son		true
a instanceof Father		true
b instanceof Son		false
c instanceof Son		tue
c instanceof Father		tue
总结:

我们在引用类型转换中,先通过instanceof运算符判断一个引用表达式所指向的实例对象是否是目标类型的实例对象,如果是,在进行类型转换,从来避免引起类型转换错误。

方法的重写override

  1. 必须保证父子类之间的方法名称相同,参数列表也相同。
  2. 子类方法的返回值必须小于等于父类方法的返回值范围。
  3. 子类方法的权限必须大于等于父类方法的权限。
class father{
    void run(){
        System.out.println("我是父类run!!!!");
    }
}
class son extend father{
    // 重写父类的run方法
    @Override
    void run(){
        System.out.println("我是子类run!!")
    }
    public static void main(String[] args) {
        son s = new son();
        s.run();
    }
}
########输出结果########
我是子类run!!!

子类构造器

在类的定义在,当前定义的类与其直接父类之间在构造方法方面存在约束,就是当前定义类的构造方法必须调用其父类的构造方法,而且该调用语句必须是当前定义的类的构造方法的第一条语句,其格式如下:

super(调用参数列表)

其中,super是关键字,表示直接父类的构造方法。这里调用的参数列表必须与其直接父类的某个构造方法的参数列表相匹配。如果在直接父类中不含与当前相匹配的构造方法,则在编译时讲出现编译错误。
如果在当前定义类的构造方法没有写上父类构造方法,则java虚拟机一般会自动在当前定义的类的构造方法的第一条语句前自动的添加不含有任何参数的直接父类构造方法的语句super(),如果这时在直接父类中没有不含任何参数的构造方法,则编译错误。

多态性

多态是指同一个方法调用,由于对象不同可能会有不同的行为。
多态要点:
1.多态是方法的多态,不是属性的多态
2.多态存在的条件:继承 方法重写 父类引用指向子类对象
3.父类引用执行子类对象后,用该父类引用调用子类重写的方法,形成多态。

静态多态:主要体现在方法重载

静态多态是指在同一个类中同名方法(成员方法或构造方法)在功能上的重载。也可以一个类对其父类同名方法在功能上的重载。
重载是指同名方法具有不同的参数列表,不同的参数列表可以是方法的参数个数不同参数的数据类型不同参数的数据类型排列顺序不同

add(int a);
add(int a,int b);
add(double a);
add(double a,double b);

动态多态:主要体现在非静态方法的重写

动态多态性指的是子类和父类的类体中均定义了具有基本相同声明的非静态成员方法。基本相同声明的方法是指子类的成员方法和其父类对应的成员方法具有相同的方法名,相同的参数个数,对应参数的类型也相同。
下面是一个动态多态性的例子:

class Father {
    void run(){
        System.out.println(1);
    }
}
public class Son extends Father {
    void run(){
        System.out.println(1);
        System.out.println(2);
    }
    public static void main(String[] args) {
        Father a = new Father();
        a.run();
        a=new Son(); // 类型转换
        a.run();
    }
}

由于动态多态性是的子类型的成员方法覆盖率父类型的成员方法,那么我们如果在子类型中调用父类被覆盖的成员方法呢?这就引入了super关键字。

super关键字
  1. 调用父类被子类重写的方法
class father {
    void run() {
        System.out.println("我是父类run!!!");
    }
}
public class son extends father {
    @Override
    void run() {
        // 调用父类被子类重写的方法
        super.run();
        System.out.println("我是子类run!!!");
    }
    public static void main(String[] args) {
        son s = new son();
        s.run();
    }
}
########输出结果########
我是父类run!!!
我是子类run!!!

在子类B中,我们重写了父类的run方法,如果在重写的run方法中我们去调用了父类的相同方法,必须要通过super关键字显示的指明出来。如果不明确出来,按照子类优先的原则,相当于还是再调用重写的run()方法,此时就形成了死循环,执行后会报java.lang.StackOverflowError异常。

  1. 调用父类被子类重定义的字段(被隐藏的成员变量)
class father {
    String name="A";
}
public class son extends father {
    String name="B";
    void getName(){
        System.out.println("子类name是:"+name);
        System.out.println("父类name是:"+super.name);
    }
    public static void main(String[] args) {
        son s = new son();
        s.getName();
    }
}
########输出结果########
子类name是:B
父类name是:A

此时子类B中有一个和父类一样的字段(也可以说成父类字段被隐藏了),为了获得父类的这个字段我们就必须加上super
我们通过super是不能访问父类private修饰的变量和方法的,因为这个只属于父类的内部成员,一个对象是不能访问它的private成员的。
3.调用父类的构造方法

class father {
    int a;
    int b;
    father(int a,int b){
        this.a=a;
        this.b=b;
    }
}
public class son extends father {
    int a;
    int b;
    int c;
    son(int a,int b,int c){
    	// 调用父类的构造器
        super(a,b);
        this.c=c;
    }
}
this关键字

1.区分成员变量和局部变量

public class Test {
    int a;
    int b;
    int c;
    Test(int a, int b1) {
        this.a = a;  // 通过this区分成员变量和局部变量
        b = b1;
    }
}

2.调用本对象中的方法

public class Test {
    void sing(){
        System.out.println("我在唱歌!");
    }
    void study(){
        this.sing(); // 通过this调用本类中sing()方法
    }
}

3.调用构造器

public class Test {
    int a;
    int b;
    int c;
    Test(int a, int b) {
        this.a = a;
        this.b = b;
    }
    Test(int a, int b, int c) {
        this(a, b);  // 通过this调用构造器
        this.c = c;
    }
}
super和this总结:

通过super关键字来实现对父类成员的访问,用来引用当前对象的父类,super与this引用不是类似的概念,因为super不是一个对象引用,不能将super赋给另一个对象变量,它只是一个指示编译器调用超类方法的特殊关键字,super指向父类,this是指向自己的引用。

动态多态性总结

动态多态性只针对非静态的成员方法,即静态成员方法不具有动态多态性。如果子类和父类均定义了相同的静态方法,并且类型为父类型的变量a指向子类的实例对象,通过变量a调用静态成员方法将调用父类的静态成员方法。

为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。
其实可以理解为就是文件夹,并且使用了树形目录的存储方式。

包的定义:

包名可以是一个标识符,也可以有若半个标识符通过“.”连接而成。通常建议包名的前几个标识符为所在单位intrnet域名顺序的倒序,最后一个标识符概括包的功能。

package 包名;
//多级包用.分开
// 建议形式
package com.sscrazy.Test

注意:

  1. package语句必须在文件中的第一条有效语句
  2. 在一个java文件中,只能有一个package

带包的编译和运行

javac -d . xxx.java
//可以使用 *.java代表当前目录下的所有源文件
//运行:
java 包名.HelloWorld

在ecplise、ideal等IDE中会自动的生成包名的结构

导包

当你需要使用一个包中的成员的时候,我们就需要在 java 程序中导入该包。
如果两个类在同一个包中,则不必导包。

第一种:
//导入单个(推荐)
import 包名;

注意:我们用谁就导谁。(尽量少用星号* 如下)

第二种:
//导入java.io下的所有类(不推荐)
import java.io.*

注意:

//前两句是否能用最后一句代替
import java.lang.*;
import java.io.*;
import java.*;//无法代替上面两句,只能指向到单层的包中

注意:使用java.lang下的内容是不用导包的。

作用:

使用某一包中对应成员,并且简化书写。
例如我们想使用java.io包下的BufferedReader
我们可以

import java.io.BufferedReader
BufferedReader in = new BufferedReader(new FileReader("foo.in"));

如果没有使用import导包
我们在使用java类库的时候,就需要写出该类的全路径名称,明显代码过于冗长

java.io.BufferedReader in = new java.io.BufferedReader(new FileReader("foo.in"));

封装性

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

优点

1)通过隐藏对象的属性来保护对象内部的状态(隐藏信息、实现细节)。
2)提高了代码的可用性和可维护性。
3)禁止对象之间的不良交互提高模块化(良好的封装能够减少耦合)。
4)可以对成员变量进行更精确的控制。
5)容易保证类内部数据间的一致性,从而提高软件的可靠性。

访问控制模式

访问控制符 在同一个类内 在同一个包内 子类 所有类
public
protected ×
default × ×
private × × ×

实现Java封装

Java中通过将数据声明为私有的(private),再提供公共的(public)方法:get()set()实现对该属性的操作,以实现下述目的:
1)隐藏一个类中不需要对外提供的实现细节;
2)使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作;
3)便于修改,增强代码的可维护性;


public class Person{
    private String name;
    public String getName(){
      return name;
    }
    }
    public void setName(String name){
      this.name = name;
    }
}

修饰词abstract、static和final

abstract(抽象)、static(静态)、final(最终)。只有static和final可以一起用。

关键字abstract

格式举例
abstract class 类名
{
	public int id=0;
	public abstract double getArea();
        public void simpleMethod(){
          System.out.println("我是普通方法");
}
}
总结:
  • 抽象类不能被实例化,如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
  • 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  • 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
  • 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
  • 抽象类的子类必须给出抽象类中的所有抽象方法的具体实现,除非该子类也是抽象类。

关键字static

类一般具有静态属性,类的构造方法不能具有静态属性。类的成员变量成员方法可以具有静态属性。如果在定义成员变量前面加上static,则该成员称为静态成员变量;如果在定义成员方法前面加上static,则该方法称为静态成员方法
在java语言中,类其实是实例对象的模板,同时类本身也可以看作一种对象,简称类对象。类的静态成员(静态成员变量和静态成员方法)隶属于该对象。可以通过类名之间访问。格式如下:

//通过类名访问类的静态成员变量
类名.静态成员变量名
//通过类名访问类的静态成员方法
类名.静态成员方法名

下面来看成员变量和成员方法的静态属性和非静态属

public class Test {
    int a;
    int b;
    static int c = 12;
    Test(){}
    Test(int a){
        this.a=2;
    }
    // 静态成员方法
    static void display() {
        System.out.println("静态变量c=" + c);
    }
    void say(){
        System.out.println("我是say(),我只能备实例对象调用");
    }
    public static void main(String[] args) {
        // 类对象Test
        // 类对象调用静态成员变量和方法
        Test.c=13;
        Test.display();
        // 类对象调用非静态成员和方法(报错)
        // Test.say();(报错)
        // System.out.println(Test.a);(报错)
        // 实例对象est
        // 实例对象调用静态、非静态成员变量和方法
        Test t1 = new Test();
        t1.a=1;
        t1.c=3;           //  甚至修改静态成员变量的值
        t1.display();
        t1.say();
        Test t2 = new Test();
        t2.a=3;
    }
}

因为静态成员方法和静态成员变量隶属于类本身所对应的对象,所以成员成员变量和静态成员方法都可以使用类本身实例对象调用。如上述Test.c Test.display()t1.c t1.display(); 但是非静态成员变量和费静态成员方法只能通过实例对象访问。如上述t1.say() t1.a等。
因为非静态成员变量和非静态成员方法都隶属于所指向的实例对象,所以不同实例对象的成员占据的内存也是不同的。比如t1.at2.a实际上是两个不同的变量。由于它们占据不同的内存,所以可以具有两个不同的值。

关键字final

关键字final只能用来修饰不具有抽象属性的类、类的成员域、接口成员方法域以及类的不具有抽象属性的方法。

  1. 修饰类:不能被继承。
  2. 修饰变量:如果同时具有static和final,则只能在定义时赋值。并且之后不能改变。如果只有final属性,则只能在定义时或者构造方法中赋值,且之后不能被改变。
  3. 修饰方法:该方法不能具有抽象属性,如果被final修饰,该方法不可被子类重写,但是可以被重载。

接口

定义

Java接口(Interface),是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
接口定义的一般形式为:

[访问控制符] interface <接口名> {
类型标识符 final 符号常量名n = 常数;
返回值类型 方法名([参数列表]);
    。。。
}

接口的特点

1、Java接口中的成员变量默认都是public,static,final类型的(都可省略),必须被显示初始化,即接口中的成员变量为常量(大写,单词之间用”_”分隔)
2、Java接口中的方法默认都是public,abstract类型的(都可省略),没有方法体,不能被实例化。
3、Java接口中只能包含public,static,final类型的成员变量和public,abstract类型的成员方法。
4、接口中没有构造方法,不能被实例化。
5、一个接口不能实现(implements)另一个接口,但它可以继承多个其它的接口。
6、Java接口必须通过类来实现它的抽象方法。
7、当类实现了某个Java接口时,它必须实现接口中的所有抽象方法,否则这个类必须声明为抽象类。
8、不允许创建接口的实例(实例化),但允许定义接口类型的引用变量,该引用变量引用实现了这个接口的类的实例。
9、一个类只能继承一个直接的父类,但可以实现多个接口,间接的实现了多继承.

接口的用法

比如,有两个及上的的类拥有相同的方法,但是实现功能不一样,就可以定义一个接口,将这个方法提炼出来,在需要使用该方法的类中去实现,就免除了多个类定义系统方法的麻烦。
举例:鸟类和昆虫类都具有飞行的功能,这个功能是相同的,但是其它功能是不同的,在程序实现的过程中,就可以定义一个接口,专门描述飞行。
实现代码如下:

interface   Flyanimal{
   void fly();
}
class   Insect {
   int  legnum=6;
}
class  Bird {
  int  legnum=2;
  void egg(){};
}
class Ant extends Insect implements  Flyanimal {
   public void fly(){
       System.out.println("Ant can  fly");
   }
}
class Pigeon  extends Bird implements  Flyanimal {
   public void fly(){
       System.out.println("pigeon  can fly");
   }
   public void egg(){
       System.out.println("pigeon  can lay  eggs ");
   }
}
public classInterfaceDemo{
   public static void main(String args[]){
     Ant a=new Ant();
     a.fly();
     System.out.println("Ant's legs are"+ a.legnum);
     Pigeon p= new Pigeon();
     p.fly();
     p.egg();
  }
}

程序运行结果:
Ant can fly
Ant’slegs are 6
pigeon can fly
pigeon can lay eggs
提示:为了解决接口中方法的升级,提供了一种默认方法,这种默认方法可以被实现类调用,不进行实现也不会报错。

interface Simple {
   public static final int num=10;
    // 省略哪个都行
    public abstract void method();
/* public void method1();
abstract void method2();
void method3();*/
  public default void methodfault(){
     System.out.println("这是接口新添加的默认的方法");
}
}
public class accomplish implement Simple{
  public static void main(String[] args) {
      InterfaceUse in =new InterfaceUse();
      in.method();
      System.out.println(in.num);
      in.methodfault();
}
@Override
public void method() {
    System.out.println("实现接口方法!!");
}
}

内部类

成员内部类

非静态内部类

1.非静态内部类必须寄存在一个外部类对象里。因此如果有一个非静态内部类对象那么一定存在对应的外部类对象。非静态内部类对象单独属于外部类的某个对象。
2.非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员。
3.非静态内部类不能有静态方法、静态属性和静态初始化块。
4.外部类的静态方法、静态代码块不能访问非静态内部类。包括不能使用非静态内部类定义变量、创建实例。
成员变量访问要点:

  1. 内部类里方法的局部变量:变量名
  2. 内部类属性:this.变量名
  3. 外部类属性:外部类名.this.变量名
package com.sscrazy.study;
public class Test{
    public static void main(String[] args) {
        // 创建内部类对象
        Outer.Inner inner = new Outer().new Inner();
        inner.show();
    }
}
class Outer {
    private final int age = 10;
    public void testOut() {
        System.out.println("testOut");
    }
    // 非静态内部类
    class Inner {
        int age = 20;
        public void show(){
            int age=30;
            System.out.println("外部类的成员变量age:"+Outer.this.age);
            System.out.println("内部类成员变量age:"+this.age);
            System.out.println("局部变量age:"+age);
        }
    }
}
########运行结果##########
外部类的成员变量age:10
内部类成员变量age:20
局部变量age:30
静态内部类

定义方法

public class Test{
    public static void main(String[] args) {
        // 创建静态内部类对象
        Outer.Inner inner =new  Outer.Inner();
        System.out.println(inner.age);
    }
}
class Outer {
    // 静态内部类
    static  class Inner {
        int age=20;
    }
}
public class Test{
    // 静态内部类
    static class Inner {
        int age=20;
    }
    public static void main(String[] args) {
        // 创建静态内部类对象
        Inner inner =new  Inner();
        System.out.println(inner.age);
    }
}

使用要点
1.当一个静态内部类的对象存在,并不一定存在对应外部类对象,因此,静态内部类的实例方法不能直接访问外部类的实例方法。
2.静态内部类看做外部类的一个静态成员。因此,外部类的方法可以通过:“”静态内部类.名字“的方式访问静态内部类的静态成员,通过new 静态内部类()访问静态内部类实例。

匿名内部类

适合那种只需要使用一次的类。比如:键盘的监听操作等。
语法:

new 父类构造器(实参类表) \实现接口(){
//匿名内部类类体
}

匿名内部类的使用

this.addWindowListener(new WindowAdapter(){
   @Override
   public void kerPressed(KeyEvent e){
       System.exit(0);
   }
});

注意:
1.匿名内部类没有访问修饰符
2.匿名内部类没有构造方法。

局部内部类

实际开发中应用极少这里不做解释。

变量作用域范围与参数传递方式

变量作用域范围

java中,变量主要包括成员域成员方法构造方法的参数变量、在方法体内定义的局部变量。变量作用域范围是指变量在java程序中的有效范围。可分为:全局作用域类作用域块作用域
1.不具有静态属性的成员域具有类作用域范围。
2.成员方法或构造方法的参数变量以及在方法体内定义的局部变量具有块作用域。
举个简单的例子:

package com.sscrazy.study;
class Time {
    public int data = 3;
}
public class Test extends Time {
    public int data = 2;
    public void method() {
        int data = 4;
        System.out.println("data=" + data);
        System.out.println("this.data=" + this.data);
        System.out.println("super.data="+ super.data);
    }
    public static void main(String[] args) {
            Test test = new Test();
            test.method();
    }
}
######运行结果########
data=4
this.data=2
super.data=3

方法调用的值传递方式

在java语言中,方法调用的参数传递方式相对简单,主要采用值传递方式。

基本数据类型值传递
package com.sscrazy.study;
public class Test {
    public static void method(int a) {
        System.out.println("在a++之前的a的值=" + a);
        a++;
        System.out.println("在a++之后的a的值=" + a);
    }
    public static void main(String[] args) {
        int i = 0;
        System.out.println("开始i的值="+i);
        Test test = new Test();
        test.method(i);
        System.out.println("最终i的值="+i);
    }
}
########运行结果#########
开始i的值=0
在a++之前的a的值=0
在a++之后的a的值=1
最终i的值=0

分析:首先变量i占据一个存储单元,它的值为整数0,在调用方法是method(i)时,java虚拟机给成员方法method的参数变量a分配一个存储单元,同时将在main成员方法中的变量i的值复制给method方法的参数变量a。从而参数变量a的值为0。在method方法中调用a++是的变量a的数值发生改变(此时变量i的值并没有改变,因为它们占据不同的存储单元)。但是在method方法调用结束后,参数变量a不能在被使用。因为超出了他的作用域范围。所以最终i的值还是为0。

引用数据类型值传递
package com.sscrazy.study;
class Time{
    public int num=10;
}
public class Test {
    public static void method(Time t) {
        System.out.println("在t.num++之前的a的值=" + t.num);
        t.num++;
        System.out.println("在t.num++之后的a的值=" + t.num);
    }
    public static void main(String[] args) {
        Time time = new Time();
        System.out.println("在method方法之前num的值="+time.num);
        method(time);
        System.out.println("在method方法之后num的值="+time.num);
    }
}
#######运行结果#########
在method方法之前num的值=10
在t.num++之前的a的值=10
在t.num++之后的a的值=11
在method方法之后num的值=11

分析:首先变量i占据一个存储单元,它的值是一个指向Time的实例对象的引用。该实例对象的成员域num的值为10。当进行method方法调用时,java虚拟机会给成员方法method的参数变量t分配一个存储单元,同时将在main成员方法中的调用参数变量time得知复制一份给成员方法的定参数变量t,那么此时变量at指向了同一个实例对象。在method方法中t.num++使得num的值发生改变(此时变量num已经发生改变,因为修改的是同一个实例对象中的成员域)。但是在method方法调用结束后,参数变量t不能在被使用。因为超出了他的作用域范围。所以最终num的值为11。

人已赞赏
其他

装WIN10的时候多了一个500M的系统保留分区,怎么删除

2020-7-10 17:52:22

Java

java 接口(interface)详解

2020-7-15 23:19:57

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
有新私信 私信列表
搜索