[JAVA] 只知对象属性,不知类属性?就算类答应,static都不答应

由浅入深——Java 类、对象、static成员

对象

在面向对象的思想中,一切事物均可以认为是对象——万物皆对象,把对象定义成包含状态和行为的一个实体,存在于现实世界中而且能够与其余实体区分开来的。对象具备状态和行为;好比:想一想你心仪的小姐姐,能够把这个小姐姐看做是一个对象,那么该对象有两方面的定义:状态和行为;状态,如身高,年龄,三围,头发(长发或者短发)等;行为,如调戏你、跳舞,玩手机等。java

经过多个相同类型的对象的状态和行为分析,能够把对象抽象成类(class);咱们把具备相同特性(状态)和行为(功能)的对象的抽象定义类,对象的抽象是类,类实例化后即是对象,类的实例是对象,类其实就是对象的数据类型,但其和基本数据类型的差别在于类是程序员为了解决某些问题而自定义的,基本数据类型是计算机中的数据存储单元。程序员

Java 对象

在Java中,对象的状态,用成员变量来描述;对象的行为,用方法来描述;故Java中类能够这样定义,语法以下:设计模式

Java 类的语法

类定义示例代码:数组

类定义示例代码

定义Java 类时有一些必要的规范须要遵照:jvm

  1. 类名一概使用英文或者国际通用的拼音符号,作到见名知义,如taobao,weixin,虽然是拼音,但倒是国际通用的,可使用;
  2. 若是类使用了public修饰符,必须保证当前java文件名称和当前类名相同,并且在一个java文件中,只能有一个public修饰的类(class);
  3. 类名首字母大写,若是类名是多个单词组成的,使用驼峰命名法,如: OperatingSystem(操做系统);

对象比较操做

先考虑下面的代码:函数

对象比较示例代码

上述示例代码运行结果为:工具

对象比较结果

为何会出现这样的结果呢?都是一样的值,为何会有不一样的比较结果?那是由于==!=这两个比较运算符在比较基本数据类型和对象对象类型时是由区别的性能

  • 对于基本数据类型来讲,比较的是值,也就是变量存储的数据内容;
  • 对于引用数据类型来讲,比较的是对象的引用,也就是其在堆内存中的地址值,每次使用new关键字建立对象,都会在堆中新开辟一块内存空间存储新建立的对象, 而且会为该内存空间生成一个惟一的地址,故内存空间不一样,内存空间的地址值也就不一样。

那么哪些数据类型时基本数据类型,哪些是引用数据类型呢?spa

  • 基本数据类型:byte、short、char、int、long、float、double、boolean
  • 引用数据类型:除基本数据类型之外的全部数据类型都是引用数据类型,包括String和基本数据类型的封装类型;

因此,若是要对对象的值作比较,就必需要是用对象的equals()方法了;这里须要注意,equals()方法并不适用于基本数据类型,对于基本数据类型的变量来讲,使用== 和 !=足够了。下面用一个例子来实践,代码以下:操作系统

"equals()方法 案例"

上述案例输出结果为:

"equals()方法案例 运行结果"

由此可看出,使用对象的equals()方法是能正确比较对象的值的,由于Integer已经自定义了equals方法了,下面是源码:

"Integer equals方法源码"

不难发现,Integer的equals()方法的底层是使用基本数据类型的值作==比较的。若是是咱们自定义的类,并且没有从新定义equals()方法呢,结果又会是怎样的,一块儿来看看:

"没有从新定义equals()方法的案例"

输出结果为:false。

由于在Java中,有一个全部引用类型都直接或者间接继承的父类,Object;所以,也能够说在java中,全部类都是Object的子类,那么,若是咱们没从新实现equals()方法,会默认调用Object的equals()方法,Object的equals()方法比较的是对象的引用,因此结果输出为false。

因此想要使用自定义对象的equals方法比较对象的值,那么就必须从新实现equals方法。

对象的打印操做

默认状况下,Java对象打印的效果是:类的名称@十六进制的hashCode,好比:

"Java对象打印的案例"

输出为:com.strlite.admin.demo.Value@79b4d0f,com.strlite.admin.demo.Value是类的名称,79b4d0f是一个十六进制的数,是对象在堆中的内存地址。

重写toString() 方法

能够经过重写toString() 方法来改变对象的打印效果:

"重写toString方法的案例"

输出为:

  • i = 13

对象的生命周期

对象的开始:每次使用new关键字建立对象,就会在内存中开辟新的空间存储对象信息,此时对象开始存在。

对象的结束:当堆中的对象,没有被任何变量所引用,此时该对象就成了垃圾,等待垃圾回收器(GC)来回收;当对象被回收后,对象被销毁,对象占用的内存空间被释放,对象的生命周期结束。

匿名对象

对象建立以后没有将其赋给某一个变量。匿名对象只是在堆中开辟一块新的内存空间,可是没有把该空间地址赋给任何变量。由于没有变量引用指向,因此匿名对象仅仅只能使用一次,通常会把匿名对象做为方法的参数传递。

  • new Integer(); // 建立的就是匿名对象

构造器

  • Integer i = new Integer();

在建立对象时使用的特殊方法,出现new 关键字以后的方法,称之为构造方法、构造器、构造函数(Constructor)

构造器的做用

  1. 用于建立对象,可是必须和 new 一块儿使用;好比:new Integer(13);
  2. 完成对象的初始化操做,能够建立带参数的构造器,为成员变量赋初始值;

构造器的特色

  1. 构造器的名称和当前所在类的名称相同;
  2. 构造器是一个特殊的方法,其没有定义返回类型,全部没必要使用void做为返回类型。 假设须要写返回类型,也应该这样写:Integer Integer(); 但没有这样的必要;
  3. 在构造器中,不须要使用return语句,其实构造器是有返回值的,会默认返回当前建立对象的引用。

若是类中没有构造器,编译器会自动建立一个默认的无参构造器

"没有构造器的案例"

咱们将上述代码通过编译,获得字节码文件,再将字节码文件反编译,反编译的结果以下:

"默认构造器案例的反编译效果"

经过反编译后的结果,不难发现,即使咱们没有建立构造器,编译器也会为咱们建立一个默认的,编译器建立的默认构造器有如下的特色:

  • 符合构造器特色;
  • 无参数的;
  • 无方法体;
  • 若是类没有使用public修饰, 则编译器问起建立的构造器也没有public修饰;使用了public修饰,则编译器建立的构造器也使用public修饰;

"默认构造器"

若是类中没有构造器,编译器会自动建立一个默认的无参构造器。可是,若是咱们显式地定义了一个构造器,则编译器再也不建立默认构造器。案例以下所示:

"编译器再也不建立默认构造器"

经过上述对比,不难发现,当类中存在一个构造器时,编译器便不会建立默认的构造器,而是使用咱们定义的构造器,由此可得出:在一个类中,至少存在一个构造器

static 修饰符

假如每一个人都有name和age两个状态,可是不一样人的name和age是不同的;也就说name和age是属于对象的。可是在生活中有些东西并非单单属于某一个对象的,而是属于整个类的,好比:每一个人都会老去、都会死。

因此,状态和行为的所属也应该有对象和类之分。 有的状态和行为应该属于对象,不一样的对象,状态和行为能够不同;而有的状态和行为应该属于类,不属于对象。为了区别与对象的状态和行为,引入static修饰符来修饰类的状态和行为。

static修饰符表示静态的,可修饰字段、方法、内部类,其修饰的成员属于类,static修饰的资源属于类级别,区别于对象级别。static的真正做用是用来区别字段、方法、内部类、初始化代码块是属于对象仍是属于类自己。

static修饰符的特色

  1. static修饰的成员(字段/方法),随着所在类的加载而加载,当JVM把字节码加载进JVM的时候,static修饰的成员已经在内存中存在了。
  2. 优先于对象的存在,对象是咱们手动经过new关键字建立出来的,static成员是JVM建立的;
  3. satic修饰的成员被该类型的全部对象所共享,该类建立的任何对象均可以访问该类的static成员;
  4. 直接使用类名访问static成员由于static修饰的成员直接属于类,不属于对象,因此能够直接使用类名访问static成员.

下面咱们经过一个案例来实践static关键字的使用:

"static关键字的使用案例"

static修改的变量称为常量,会长时间存在于JVM内存中,因此JVM也会为它分配必定的存储空间,如下即是static常量在jvm 中的内存模型:

"static常量在jvm 中的内存模型"

JVM会将静态变量存储在方法区中,以便于及时调用;并保证其可以长时间存储于JVM中。

类成员和实例成员的访问

类中的成员:字段,方法,内部类。

  • 类成员:使用static修饰的成员,直接属于类,经过类名.static成员来访问;
  • 实例成员:没有使用static修饰的成员,实例成员只属于对象, 经过对象来访问非static字段和非static方法;

通常状况下,类成员只能访问类成员,实例成员只能访问实例成员;但深究发现,对象其实能够访问类成员,可是底层依然使用类名访问的。

static方法

static方法中,只能调用static成员;非static方法,能够访问静态成员,也能够访问实例成员;

那何时定义成static的字段和方法:

  • 若是这个一个状态/行为属于整个事物(类),被全部对象所共享,就直接使用static修饰;
  • 在开发中,每每把工具方法使用static修饰,好比:数组中经常使用的java.util.Arrays中的方法;

若是不使用static修饰,则这些方法属于该类的对象,咱们得先建立对象才能调用方法,在开发中工具对象只须要一份便可,可能建立N个对象,此时能够考虑使用单例设计模式。

"成员生命周期"

类成员的使用

好处:对对象的共享数据进行单独空间的存储,节省空间,没有必要每个对象中都存储一份,能够直接被类名调用。

弊端:生命周期过长。

局部变量初始化

局部变量定义后,必须显式初始化后才能使用,由于JVM不会为局部变量执行初始化操做。这就意味着,定义局部变量后,JVM并未为这个变量分配内存空间。直到程序为这个变量赋值时,系统才会为局部变量分配内存,并将初始值保存到该内存中。

局部变量不属于任何类或实例,所以它是保存在其所在方法的栈帧内存中。

  • 基本数据局部变量:基本数据类型变量的值会直接保存到该变量所对应的内存中。
  • 引用数据局部变量:变量内存中存的是堆中对象的地址,经过该地址引用到该变量实际指向的堆里的对象。

栈帧内存中的变量随方法或代码块的运行结束而销毁,无须JVM回收。

一点小建议

  • 开发中应该尽可能缩小变量的做用范围,如此在内存中停留时间越短,性能也就更高。
  • 合理使用static修饰,通常只有定义工具方法的时候使用;
  • static方法须要访问的变量,只有该变量确实属于类,才使用static修饰字段;
  • 尽可能使用局部变量;

完结。老夫虽不正经,但老夫一身的才华

相关文章
相关标签/搜索