初识类与对象

1、面向对象

  面向对象(Object Oriented)是一种新兴的程序设计方法,或者是一种新的程序设计规范(paradigm),其基本思想是使用对象、类、继承、封装、多态等基本概念来进行程序设计。从现实世界中客观存在的事物(即对象)出发来构造软件系统,并且在系统构造中尽可能运用人类的自然思维方式。简单一句话就是,面向对象是从人的角度出发解决问题。

2、对象

  对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。一个对象由一组属性和对这组属性进行操作的一组服务组成。简而言之,对象就是现实事物的抽象表达。对象的属性就是现实事物的属性(名词),对象的方法就是现实事物的行为(动词)。例如:一辆轿车可以抽象为一个对象,轿车有四个车轮的属性,可以抽象为轿车对象的属性,轿车加速减速等动作,可以抽象为轿车对象的方法。

  类的实例化可生成对象(通常使用new语句实例化一个类,这个方式也可以叫获取类的实例),一个对象的生命周期包括三个阶段:生成、使用、消除。

  当不存在对一个对象的引用时,该对象成为一个无用对象。Java的垃圾收集器自动扫描对象的动态内存区,把没有引用的对象作为垃圾收集起来并释放。当系统内存用尽或调用System.gc( )要求垃圾回收时,垃圾回收线程与系统同步运行。


3、类

  类是具有相同属性和方法的一组对象的集合,它为属于该类的所有对象提供了统一的抽象描述,其内部包括属性和方法两个主要部分。在面向对象的编程语言中,类是一个独立的程序单位,它应该有一个类名并包括属性和方法两个主要部分。简而言之,类是对象的模板。例如:小明有嘴,能吃饭(小明看成一个对象)。小红有嘴,能吃法(小红也看成一个对象)。小明小红这两个对象都属于人类这个类,人类这个模板就有嘴这个属性和吃法的方法。

  Java中的类实现包括两个部分:类声明和类体。

1:类声明

类修饰词 class 类名{}

  2:类体

类体就是类声明的{}的部分。类体一般包括成员变量和成员方法。

成员变量就是属性,可以理解为对象的名词。

成员方法就是行为,可以理解为对象的动词。

例如:

public class People {//声明人这个类
	String mouth;//成员属性:人都具有嘴这个属性(名词)

	void eat() {//成员方法:人都具有吃饭这个行为(动词)

	}
}

4:访问修饰词

  • 访问控制权限

访问修饰符可以修饰类,成员变量及成员方法,访问修饰符具体访问权限参照下表:

public:public修饰的类或成员变量、成员方法,可以被所有的类访问。

protected:protected修饰的类或成员变量、成员方法,可以被这个类本身、它的子类(包括同一个包中以及不同包中的子类)和同一个包中的所有其他的类访问。

缺省的:不加任何访问权限限定的成员及类属于缺省的(default)访问状态,可以被这个类本身和同一个包中的类所访问。

private:private修饰的类或成员变量、成员方法,只能被这个类本身访问。

  • 访问控制和继承

请注意以下方法继承(继承概念的另附博文介绍)的规则:
父类中声明为public的方法在子类中也必须为public。
父类中声明为protected的方法在子类中要么声明为protected,要么声明为public。不能声明为private。
父类中缺省的修饰符声明的方法,能够在子类中声明为private。
父类中声明为private的方法,不能够被继承。

5:静态修饰符(static)

类中static修饰的成员属性(变量),成员方法叫做静态属性(变量),静态方法。因为静态成员属于类本身,在类加载时会自动创建,所以调用时可以用类名.语
句直接调用。

public class People {

	// 调用可以为People.mouth
	static String mouth;// 静态属性
	
	// 调用可以为People.eat()
	static void eat() {// 静态方法

	}
}


6:最终属性final

变量和常量

在程序中存在大量的数据来代表程序的状态,其中有些数据在程序的运行过程中值会发生改变,有些数据在程序运行过程中值不能发生改变,这些数据在程序中分别被叫做: 变量和常量。
在实际的程序中,可以根据数据在程序运行中是否发生改变,来选择应该是使用变量代表还是常量代表。
  • 变量
    变量代表程序的状态。程序通过改变变量的值来改变整个程序的状态,或者说得更大一些,也就是实现程序的功能逻辑。
    为了方便的引用变量的值,在程序中需要为变量设定一个名称,这就是变量名。例如在2D游戏程序中,需要代表人物的位置,则需要2个变量,一个是x坐标,一个是y坐标,在程序运行过程中,这两个变量的值会发生改变。
    由于Java语言是一种强类型的语言,所以变量在使用以前必须首先声明,在程序中声明变量的语法格式如下:
    数据类型 变量名称;
    例如:int x;
    在该语法格式中,数据类型可以是Java语言中任意的类型,包括前面介绍到的基本数据类型以及后续将要介绍的复合数据类型。变量名称是该变量的标识符,需要符合标识符的命名规则,在实际使用中,该名称一般和变量的用途对应,这样便于程序的阅读。数据类型和变量名称之间使用空格进行间隔,空格的个数不限,但是至少需要1个。语句使用“;”作为结束。
    也可以在声明变量的同时,设定该变量的值,语法格式如下:
    数据类型 变量名称 = 值;
    例如:int x = 10;
    在该语法格式中,前面的语法和上面介绍的内容一致,后续的“=”代表赋值,其中的“值”代表具体的数据。在该语法格式中,要求值的类型需要和声明变量的数据类型一致。
       在程序中,变量的值代表程序的状态,在程序中可以通过变量名称来引用变量中存储的值,也可以为变量重新赋值。例如:
    int n = 5;
    n = 10;
    在实际开发过程中,需要声明什么类型的变量,需要声明多少个变量,需要为变量赋什么数值,都根据程序逻辑决定,这里列举的只是表达的格式而已。
  •     常量
    常量代表程序运行过程中不能改变的值。
    常量在程序运行过程中主要有2个作用:
         1. 代表常数,便于程序的修改(例如:圆周率的值)
         2. 增强程序的可读性(例如:常量UP、DOWN、LEFT和RIGHT分辨代表上下左右,其数值分别是1、2、3和4)
    常量的语法格式和变量类型,只需要在变量的语法格式前面添加关键字final即可。在Java编码规范中,要求常量名必须大写。
    则常量的语法格式如下:
    final 数据类型 常量名称 = 值;
    final 数据类型 常量名称1 = 值1, 常量名称2 = 值2,……常量名称n = 值n;
    例如:
    final double PI = 3.14;
    final char MALE=‘M’,FEMALE=‘F’;
    在Java语法中,常量也可以首先声明,然后再进行赋值,但是只能赋值一次,示例代码如下:
 final int UP;
    UP = 1;

 final 关键字

     final 用于声明属性(常量),方法和类,分别表示属性一旦被分配内存空间就必须初始化(不会有默认初始化,局部变量也是如此,默认初始化只有普通的非final成员属性,对于static(无final修饰)类变量,类连接时候有默认初始化,对于像private int a;在类实例化时,构造函数默认初始为0,总之,变量必须初始化后方可用,这是java的安全之一。final这个关键字的含义是“这是无法改变的”或者“终态的”;

    那么为什么要阻止改变呢?

    java语言的发明者可能由于两个目的而阻止改变:

    1).效率问题:

    jdk中的某些类的某些方法,是不允许被用户覆盖的,设计者可能认为,所用方法已经是最好的方法,用户私自覆盖,或是由于疏忽而覆盖,就会影响JVM或是系统的系能;

    2). 设计所需:

    众所周知,有些情况必须使用final关键字,比如方法中的匿名内部类的参数传递(例如检查银行卡密码的方法,就不能被重写,需要final关键字修饰)

    【修饰变量】:

     final成员变量表示常量,只能被赋值一次,赋值后值不再改变。

    【修饰方法】:

     final方法不能被子类方法覆盖,但可以被继承。

    【修饰类】:

     final类不能被继承,没有子类,final类中所有方法都是final的。(如String类)
  • 被final修饰而没有被static修饰的类的属性变量只能在两种情况下初始化:(必须初始化)
a.在它被声明的时候赋值 
b.在构造函数里初始化

解释:                                                                                                                                                                                    

  当这个属性被修饰为final,而非static的时候,它属于类的实例对象的资源(实例常量),当类被加载进内存的时候这个属性并没有给其分配内存空间,而只是 定义了一个变量a,只有当类被实例化的时候这个属性才被分配内存空间,而实例化的时候同时执行了构造函数,所以属性被初始化了,也就符合了当它被分配内存 空间的时候就需要初始化,以后不再改变的条件.

  • 被static修饰而没有被final修饰的类的属性变量只能在两种情况下初始化:(可以不初始化)
a.在它被声明的时候赋值
b.在静态或非静态快里初始化

解释:

当类的属性被同时被修饰为static时候,他属于类的资源(类变量),在类加载后,进行连接时候,分三步: 先验证;然后准备,准备时,先分配内存,接着默认初始化;可以进行解析。最后,进行类初始化,类初始化前,必须保证它的父类已经初始化了,所以最先初始化的是超类,对于接口,不必初始其父接口。类初始化时,它把类变量初始化语句及静态初始化语句放到类初始化方法中,所以,如果无此两种语句,也就没<clinit>类初始化方法,而构造函数是在当类被实例化的时候才会执行,所以用构造函数,这时候这个属性没有被初始化.程序就会报错.而static块是类被加载的时候执行,且只执行这一次,所以在 static块中可以被初始化.

  • 同时被final和static修饰的类的属性变量只能在两种情况下初始化:(必须初始化)
a.在它被定义的时候
b.在类的静态块里初始化 
c.特别对于初始化时候调用抛出异常的构造函数,初始时候注意,特别是在实现单例模式时(只能这么初始化)

解释:
当类的属性被同时被修饰为static和final的时候,他属于类的资源(类常量),那么就是类在被加载进内存的时候(也就是应用程序启动的时候)就要已经为此属性分配了内存,所以此时属性已经存在,它又被final修饰,所以必须在属性定义了以后就给其初始化值.而构造函数是在当类 被实例化的时候才会执行,所以用构造函数,这时候这个属性没有被初始化.程序就会报错.而static块是类被加载的时候执行,且只执行这一次,所以在 static块中可以被初始化.

java中的 final变量==常量

【final变量的变与不变】:final表示变量的值或引用不变
    有人说final变量在赋值后就不可变,此变量可以是基本数据类型+String或者是对象
    那么这个不变到底指的是什么呢?
    这个不变指的是引用,是地址,而所引用的对象的内容仍然是可变的。注:如果为对象,注意此时类初始化条件
    就是说,这个final变量永远指向某个对象,是一个常量指针,而不是指向常量的指针。

【final关键字的具体应用】:
    【final+变量】:
        在实际应用中,这种形式是非常少见的。
        比如logger是可以的,但是貌似并不是非常实用,或许用户仍然希望通过setter来改变logger变量。
    【static+final+变量】:
        常量。经常使用。
    【final+方法】:
        JDK中常用。
    【final+类】:
        helper类经常使用。
    【final用于匿名内部类的参数传递】:
        在多线程测试时,经常使用。
    【final用于方法的参数】:
        并不常用。  

延伸:

在interface里面的变量都是public static final 的。所以你可以这样写:

public static final int i=10;等价于 int i=10;(可以省略掉一部分)注意在声明的时候要给变量赋予初值

解释:

       首先要弄清接口的含义.接口就是提供一种统一的’协议’,而接口中的属性也属于’协议’中的成员.它们是公共的,静态的,最终的常量.相当于全局常量.

   抽象类是不’完全’的类,相当于是接口和具体类的一个中间层.即满足接口的抽象,也满足具体的实现.

       如果接口可以定义变量,但是接口中的方法又都是抽象的,在接口中无法通过行为来修改属性。有的人会说了,没有关系,可以通过实现接口的对象的行为来修改接口中的属性。这当然没有问题,但是考虑这样的情况。如果接口A中有一个public访问权限的静态变量a。按照java的语义,我们可以不通过实现接口的对象来访问变量a,通过A.a = xxx;就可以改变接口中的变量a的值了。正如抽象类中是可以这样做的,那么实现接口A的所有对象也都会自动拥有这一改变后的a的值了,也就是说一个地方改变了a,所有这些对象中a的值也都跟着变了。如果可以修改值:这和抽象类有什么区别呢,怎么体现接口更高的抽象级别呢,怎么体现接口提供的统一的协议呢,那还要接口这种抽象来做什么呢?所以接口中不能出现变量,如果有变量,就和接口提供的统一的抽象这种思想是抵触的。所以接口中的属性必然是常量,只能读不能改,这样才能为实现接口的对象提供一个统一的属性。

       通俗的讲,认为是要变化的东西,就放在实现中,不能放在接口中去,接口只是对一类事物的属性和行为更高层次的抽象。对修改关闭,对扩展(不同的实现implements)开放,接口是对开闭原则的一种体现。(接口具体概念,另附博文介绍)

7:方法

真如前文所述,类体是由方法的实现也包括两部分内容:方法声明和方法体。

  • 方法声明

按照方法有无返回值,有无参数,可以分为四类

  1. 无参无返回值
    基本结构:修饰符 void 方法名(){} 其中void 关键字表示无返回值,方法名后的()为空表示无参数。例如:
    	public void eat() {// 无参无返回值的成员方法
    
    	}
  2. 无参有返回值
    基本结构:修饰符 返回值类型 方法名(){} 其中{}内会有return关键字,表示返回值,方法名后的()为空表示无参数。例如:
    	public int eat() {// 无参有返回值的成员方法
    		return 1;
    	}
  3. 有参无返回值
    基本结构:修饰符 void 方法名(参数类型 参数名){} 其中void 关键字表示无返回值。例如:
    	public void eat(String a) {// 有参无返回值的成员方法
    		
    	}

  4. 有参有返回值
    基本结构:修饰符 返回值类型 方法名(参数类型 参数名){} 。例如:
    	public int eat(String a) {// 有参有返回值的成员方法
    		return 1;	
    	}

延伸:

Java中的参数传递方式
无论是什么语言,要讨论参数传递方式,就得从内存模型说起,这里主要从内存模型来说参数传递更为直观一些。闲言少叙,下面通过内存模型的方式来讨论一下Java中的参数传递。
这里的内存模型涉及到两种类型的内存:栈内存(stack)和堆内存(heap)。基本类型作为参数传递时,传递的是这个值的拷贝。无论怎么改变这个拷贝,原值是不会改变的。看下边的一段代码,然后结合内存模型来说明问题:

public class Text {

	public static void add(int param) {
		param = 100;
	}

	public static void main(String[] args) {
		int num = 30;
		System.out.println("调用add方法前num=" + num);
		add(num);
		System.out.println("调用add方法后num=" + num);
	}
}

运行结果:


程序运行的结果也说明这一点,无论你在add()方法中怎么改变参数param的值,原值num都不会改变。下边通过内存模型来分析一下。

当执行了int num = 30;这句代码后,程序在栈内存中开辟了一块地址为AD8500的内存,里边放的值是30,内存模型如下图:


执行到add()方法时,程序在栈内存中又开辟了一块地址为AD8600的内存,将num的值30传递进来,此时这块内存里边放的值是30,执行param = 100;后,AD8600中的值变成了100。内存模型如下图:


地址AD8600中用于存放param的值,和存放num的内存没有任何关系,无论怎么改变param的值,实际改变的是地址为AD8600的内存中的值,而AD8500中的值并未改变,所以num的值也就没有改变。
以上是基本类型参数的传递方式,下来讨论一下对象作为参数传递的方式。

先运行以下代码:

public class Text {

	public static void main(String[] args) {
		String[] array = new String[] { "huixin" };
		System.out.println("调用reset方法前array中的第0个元素的值是:" + array[0]);
		reset(array);
		System.out.println("调用reset方法后array中的第0个元素的值是:" + array[0]);
	}

	public static void reset(String[] param) {
		param[0] = "hello, world!";
	}
}
运行结果:

当对象作为参数传递时,传递的是对象的引用,也就是对象的地址。下边用内存模型图来说明。


当程序执行了String[] array = new String[] {“huixin”}后,程序在栈内存中开辟了一块地址编号为AD9500内存空间,用于存放array[0]的引用地址,里边放的值是堆内存中的一个地址,示例中的值为BE2500,可以理解为有一个指针指向了堆内存中的编号为BE2500的地址。堆内存中编号为BE2500的这个地址中存放的才是array[0]的值:huixin。
当程序进入reset方法后,将array的值,也就是对象的引用BE2500传了进来。这时,程序在栈内存中又开辟了一块编号为AD9600的内存空间,里边放的值是传递过来的值,即AD9600。可以理解为栈内存中的编号为AD9600的内存中有一个指针,也指向了堆内存中编号为BE2500的内存地址,如图所示:


这样一来,栈内存AD9500和AD9600(即array[0]和param的值)都指向了编号为BE2500的堆内存。
在reset方法中将param的值修改为hello, world!后,内存模型如下图所示:


改变对象param的值实际上是改变param这个栈内存所指向的堆内存中的值。param这个对象在栈内存中的地址是AD9600,里边存放的值是BE2500,所以堆内存BE2500中的值就变成了hello,world!。程序放回main方法之后,堆内存BE2500中的值仍然为hello,world!,main方法中array[0]的值时,从栈内存中找到array[0]的值是BE2500,然后去堆内存中找编号为BE2500的内存,里边的值是hello,world!。所以main方法中打印出来的值就变成了hello,world!

无论是基本类型作为参数传递,还是对象作为参数传递,实际上传递的都是值,只是值的的形式不用而已。第一个示例中用基本类型作为参数传递时,将栈内存中的值30传递到了add方法中。第二个示例中用对象作为参数传递时,将栈内存中的值BE2500传递到了reset方法中。当用对象作为参数传递时,真正的值是放在堆内存中的,传递的是栈内存中的值,而栈内存中存放的是堆内存的地址,所以传递的就是堆内存的地址。这就是它们的区别。

注意,在Java中,String是一个引用类型,但是在作为参数传递的时候表现出来的却是基本类型的特性,即在方法中改变了String类型的变量的值后,不会影响方法外的String变量的值。

  • 方法体

  方法体是对方法的实现(方法声明中的{}内),如果方法声明为有返回值,方法体中的返回值须与声明返回类型一致,或者完全相同,或是其子类。当返回类型是接口时,返回值必须实现该接口。

方法体还包括局部变量(不懂可以先行跳过,博客后文详细介绍)的声明以及所有合法的Java指令。方法体中声明的局部变量的作用域在该方法内部。若局部变量与类的成员变量同名,则类的成员变量被隐藏。

  为了区别参数和类的成员变量,必须使用this关键字。this用在一个方法中引用当前对象,它的值是调用该方法的对象。

this关键字

  1. this 关键字用来表示当前对象本身,或当前类的一个实例,通过 this 可以调用本对象的所有方法和属性。例如:
    public class Text {
    	public int x = 10;
    	public int y = 15;
    
    	public void sum() {
    		// 通过 this 获取成员变量
    		int z = this.x + this.y;
    		System.out.println("x + y = " + z);
    	}
    
    	public static void main(String[] args) {
    		Text obj = new Text();
    		obj.sum();
    	}
    }
    运行结果:
    x + y = 25
    上面的程序中,obj 是 Text类的一个实例,this 与 obj 等价,执行 int z = this.x + this.y;,就相当于执行 int z = obj.x + obj.y;。
    注意:this 只有在类实例化后才有意义。
  2. 成员变量与方法内部的变量重名时,希望在方法内部调用成员变量。这时候只能使用this,例如:
    public class Text {
    	public String name;
    	public int age;
    
    	public Text(String name, int age) {
    		this.name = name;
    		this.age = age;
    	}
    
    	public void say() {
    		System.out.println("网站的名字是" + name + ",已经成立了" + age + "年");
    	}
    
    	public static void main(String[] args) {
    		Text obj = new Text("百度", 3);
    		obj.say();
    	}
    }

    结果:
    网站的名字是百度,已经成立了3年
    形参的作用域是整个方法体,是局部变量。在Demo()中,形参和成员变量重名,如果不使用this,访问到的就是局部变量name和age,而不是成员变量。在 say() 中,没有使用 this,因为成员变量的作用域是整个实例,当然也可以加上 this:
    	public void say(){
    		System.out.println("网站的名字是" + this.name + ",已经成立了" + this.age + "年");
    	}
    Java 默认将所有成员变量和成员方法与 this 关联在一起,因此使用 this 在某些情况下是多余的。
  3. 作为方法名来初始化对象:调用本类的其它构造方法(不懂可以先行跳过,博客后文详细介绍),它必须作为构造方法的第一句。例如:
    public class Text {
    	public String name;
        public int age;
       
        public Text(){
           this("百度", 3);
        }
      
        public Text(String name, int age){
           this.name = name;
           this.age = age;
        }
      
        public void say(){
           System.out.println("网站的名字是" + name + ",已经成立了" + age + "年");
        }
      
        public static void main(String[] args) {
        	Text obj = new Text();
           obj.say();
        }
    }

    结果:
    网站的名字是百度,已经成立了3年
    值得注意的是:
    在构造方法中调用另一个构造方法,调用动作必须置于最起始的位置。
    不能在构造方法以外的任何方法内调用构造方法。
    在一个构造方法内只能调用一个构造方法。
    上述代码涉及到方法重载(不懂可以先行跳过,博客后文详细介绍)。
  4. 作为参数传递:需要在某些完全分离的类中调用一个方法,并将当前对象的一个引用作为参数传递时。例如:
    public class Text {
    	public static void main(String[] args) {
    		B b = new B(new A());
    	}
    }
    
    class A {
    	public A() {
    		new B(this).print(); // 匿名对象
    	}
    
    	public void print() {
    		System.out.println("Hello from A!");
    	}
    }
    
    class B {
    	A a;
    
    	public B(A a) {
    		this.a = a;
    	}
    
    	public void print() {
    		a.print();
    		System.out.println("Hello from B!");
    	}
    }

    运行结果:
    Hello from A!
    Hello from B!
    匿名对象就是没有名字的对象。如果对象只使用一次,就可以作为匿名对象,代码中 new B(this).print(); 等价于 ( new B(this) ).print();,先通过 new B(this) 创建一个没有名字的对象,再调用它的方法。

 
 8:构造方法(构造器)

构造方法是一个特殊的方法。Java 中的每个类都有构造方法,用来初始化该类的一个对象。

默认隐藏的构造方法为:public void 类名(){}

构造方法具有和类名相同的名称,而且不返回任何数据类型。

构造方法只能由new运算符调用。

延伸:

Java类加载及实例化的调用顺序
  1. 没有继承的情况
    单独一个类的情况下,初始化顺序为依次为静态数据,成员变量,被调用的构造函数。其中静态数据只会初始化一次。
    public class Text {
    	public static void main(String[] args) {
    		Son son = new Son();// 调用Son这个类
    	}
    }
    
    class Son {
    	public Son() {// 构造方法
    		System.out.println("加载构造方法.");
    	}
    
    	public Height height = new Height(1.8f);// 成员变量
    
    	public static Gender gender = new Gender(true);// 静态变量
    }
    
    class Height {
    	public Height(float height) {
    		System.out.println("加载成员变量:初始身高为: " + height + " 米.");
    	}
    }
    
    class Gender {
    	public Gender(boolean isMale) {
    		if (isMale) {
    			System.out.println("加载静态变量:他是男孩!");
    		} else {
    			System.out.println("加载静态变量:她是女孩!");
    		}
    	}
    }
    运行结果:
  2. 存在继承的情况
    祖父类代码:

public class Grandpa {
	public Grandpa() {
		System.out.println("加载祖父类构造方法。");
	}

	private Height height = new Height(1.5f,"祖父类");

	public static Gender gender = new Gender(true, "祖父类");
}
父类代码:

public class Father extends Grandpa {
    public Father() {
        System.out.println("加载父类构造方法");
    }

    private Height height = new Height(1.6f,"父类");

    public static Gender gender = new Gender(true, "父类");
}
子类代码:

public class Son extends Father {

    public Son() {//构造方法
       
        System.out.println("加载子类构造方法。");
    }

    private Height height = new Height(1.8f,"子类");//成员变量

    public static Gender gender = new Gender(true, "子类");//静态变量
}

测试类代码:

public class Text {
	public static void main(String[] args) {
		Son son = new Son();// 加载Son这个有继承关系的类
	}
}

class Height {
	public Height(float height, String identify) {
		System.out.println("加载" + identify + "的成员变量:" + "初始身高为:" + height + " 米。");
	}
}

class Gender {
	public Gender(boolean isMale) {
		if (isMale) {
			System.out.println("他是男孩!");
		} else {
			System.out.println("她是女孩!");
		}
	}

	public Gender(boolean isMale, String identify) {
		if (isMale) {
			System.out.println("加载" + identify + "的静态变量:" + " 男孩.");
		} else {
			System.out.println("加载" + identify + "的静态变量:" + " 女孩.");
		}
	}
}
运行结果:


总结:

1:静态优先。类加载第一步,先父类静态变量,再子类静态变量。当父类存在父类时,递归至基类(先加载祖先的静态变量,再按继承链依次加载静态变量)。

2:静态变量加载完毕,先父类加载全部。在加载子类全部。当父类存在父类时,递归至基类(先加载祖先的全部,再按继承链依次加载其子类的全部)。

3:在同等类非静态级别中,成员变量优先于构造方法。

4:类的加载顺序与代码的前后顺序无关。

9:变量作用域

在Java中,变量的作用域分为四个级别:类级、对象实例级、方法级、块级。
  • 类级变量又称全局级变量或静态变量,需要使用static关键字修饰。类级变量在类定义后就已经存在,占用内存空间,可以通过类名来访问,不需要实例化。
  • 对象实例级变量就是成员变量,实例化后才会分配内存空间,才能访问。
  • 方法级变量就是在方法内部定义的变量,就是局部变量,只能在该方法内访问。
  • 块级变量就是定义在一个块内部的变量,变量的生存周期就是这个块,出了这个块就消失了,比如 if、for 语句的块。块是指由大括号包围的代码。
    public class Text{
        public static String name = "百度";  // 类级变量
        public int i; // 对象实例级变量
        // 属性块,在类初始化属性时候运行
        {
            int j = 2;// 块级变量
        }
        public void test1() {
            int j = 3;  // 方法级变量
            if(j == 3) {
                int k = 5;  // 块级变量
            }
            // 这里不能访问块级变量,块级变量只能在块内部访问
            System.out.println("name=" + name + ", i=" + i + ", j=" + j);
        }
        public static void main(String[] args) {
            // 不创建对象,直接通过类名访问类级变量
            System.out.println(Text.name);
           
            // 创建对象并访问它的方法
            Text t = new Text();
            t.test1();
        }
    }
    运行结果:

10:instanceof 运算符

instanceof 运算符用来判断一个变量所引用的对象的实际类型,注意是它引用的对象的类型,不是变量的类型。演示代码:
public final class Text{
    public static void main(String[] args) {
        // 引用 People 类的实例
    	Person obj = new Person();
        if(obj instanceof Object){
            System.out.println("我是一个对象");
        }
        if(obj instanceof Person){
            System.out.println("我是人类");
        }
        if(obj instanceof Teacher){
            System.out.println("我是一名教师");
        }
        if(obj instanceof President){
            System.out.println("我是校长");
        }
        System.out.println("-----------");  // 分界线
       
        // 引用 Teacher 类的实例
        obj = new Teacher();
        if(obj instanceof Object){
            System.out.println("我是一个对象");
        }
        if(obj instanceof Person){
            System.out.println("我是人类");
        }
        if(obj instanceof Teacher){
            System.out.println("我是一名教师");
        }
        if(obj instanceof President){
            System.out.println("我是校长");
        }
    }
}
class Person{}
class Teacher extends Person{ }
class President extends Teacher{ }
运行结果:
我是一个对象
我是人类
———–
我是一个对象
我是人类
我是一名教师

总结:如果变量引用的是当前类或它的子类的实例,instanceof 返回 true,否则返回 false。




















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值