目录
基本概念
类与对象的定义与使用
对象实例化操作初步分析
引用传递分析
引用与垃圾产生分析
深入分析类与对象
成员属性封装
构造方法与匿名对象
this关键字
使用this调用当前属性
this调用本类方法
实战:简单Java类
static关键字
static定义属性
static定义方法
static应用案例
代码块
普通代码块
构造代码块
静态代码块
学习笔记
面向过程对于一个问题的解决方案,一般不会做出重用的设计思想。
面向对象,主要的设计形式为模块化设计,并且 可以进行重用配置。在面向对象的设计中更多考虑的是标准。在使用的时候根据标准进行拼装。有三个特征:
面向对象在程序开发中的三个步骤:
面向对象核心组成:类与对象,如:汽车设计图纸与一个具体的车。
类是对某一类事物的共性的概念,而对象描述是一个具体的产物。类是一个模板,对象是可以使用的实例。先有类后有对象。在类中一般有两个组成:
在Java中类是一个独立的结构体,所以使用class进行定义,而在类中主要组成是成员属性与操作方法,成员属性是一个个变量,操作方法是一个个可以重复执行的代码。
范例:定义一个类
class Person{// 定义一个类
// 成员属性
String name ;
int age ;
// 操作方法
public void tell(){
System.out.println("姓名:" + name + " " + "年龄:" + age ) ;
}
}
分析: 成员属性:name、age; 操作方法: tell(注意:通过类引用所以不加static)
类必须通过对象来使用,产生对象的语法如下:
当实例化对象过后,需要通过对象进行类中操作方法的调用,此时有两种调用方式:
范例:使用对象操作类
class Person{// 定义一个类
// 成员属性
String name ;
int age ;
// 操作方法
public void tell(){
System.out.println("姓名:" + name + " " + "年龄:" + age ) ;
}
}
public class JavaDemo{
public static void main( String arges[] ){
Person per = new Person() ; // 声明并实例化
per.age = 18 ;
per.name = "张三" ;
per.tell() ; // 操作方法调用
}
}
如果没有进行对象成员属性的设置,声明实例化过后成员属性为默认值(数据类型的默认值 或 编写类时设置的);
Java中的类时属于引用数据类型,因此需要进行内存的管理,在进行操作的时候也会发生内存关系的变化。下面进行简单分析。
进行内存分析,需要给出常用的内存空间:
产生对象的语法有两种:声明并实例化、分步骤完成
下面对上文代码进行分析:声明并实例化
分步骤完成:
public class JavaDemo{
public static void main( String arges[] ){
Person per = null ; // 声明对象 没有指向堆内存
per = new Person() ;// 实例化对象
per.age = 18 ;
per.name = "张三" ;
per.tell() ; // 操作方法调用
}
}
注意:所有的对象在调用类中属性和方法时,必须实例化完成之后才能执行。
范例:错误的代码
public class JavaDemo{
public static void main( String arges[] ){
Person per = null ; // 申明对象
per.age = 18 ;
per.name = "张三" ;
per.tell() ; // 操作方法调用
}
}
代码之中只进行了声明,并没有进行实例化,所以无法调用。错误中NullPointerEcception(明确指出空指向异常),就是在堆内存没有开辟后产生的问题,并且只有引用数据类型才会出现这种错误。
类本省属于引用数据类型,既然是引用数据类型,就牵扯到内存的引用传递,所谓的引用传递的本质是:同一块堆内存被不同的栈内存指向,也可以更换指向。
范例:定义一个引用传递分析的程序
public class JavaDemo{
public static void main( String arges[] ){
Person per1 = new Person() ; // 申明并实例化
per1.age = 18 ;
per1.name = "张三" ;
Person per2 = per1 ; //引用传递
per2.age = 80 ;
per1.tell() ; // 操作方法调用
// 结果为80
}
}
结果:80
这个时候的引用传递是在主方法中定义的,也可以通过方法实现引用传递处理。
范例:应用方法实现引用传递处理
public class JavaDemo{
public static void main( String arges[] ){
Person per = new Person() ; // 申明并实例化
per.age = 18 ;
per.name = "张三" ;
change(per) ; // 引用传递 等价于 Person temp = per
per.tell() ; // 操作方法调用
}
public static void change(Person temp){
temp.age = 80 ;
}
}
结果 : 80
与之前最大的区别在于,此实的程序将Person类的实例化对象(内存地址,数值)传递给change()方法之中,由于传递的是Person类型,所以change()接受的也是Person类型。
引用传递可以发生在方法上,一定要观察方法的参数类型,同时也要观察方法的执行过程。
因此所有的引用传递本质上是堆内存的调戏游戏。但是对于引用传递如果处理不当会造成垃圾的产生。将对垃圾的产生进行简单的分析。
范例:
public class JavaDemo{
public static void main( String arges[] ){
Person per1 = new Person() ; // 申明并实例化
Person per2 = new Person() ;
per1.age = 18 ;
per1.name = "张三" ;
per2.age = 19 ;
per2.name = "李四" ;
per2 = per1 ; // 引用传递
per2.age = 80 ;
per1.tell() ; // 操作方法调用
}
}
此时已经发生了引用传递,并且成功的完成了引用传递操作。下面进行内存分析:
所谓的垃圾空间指的是没有栈内存指向的堆内存空间,所有的垃圾空间将由GC(GarbageCollector(垃圾收集器))不定期进行回收,并且释放垃圾空间。但是如果垃圾过多一定会影响GC的回收性能,从而降低整体的程序性能。所以在程序开发中,垃圾的产生越少越好。
一个栈内存只包含一个堆内存的地址数据,如果发生更改,之前的地址数据将从此从栈内存中消失。
类是由属性与方法组成,一般而言方法都是对外服务的,所以不会进行封装处理,而对于属性由于其需要较高的安全型=性,所以往往需要进行其保护,这个时候就需要采用封装性对属性进行保护。
在默认情况下,在类中属性可以通过其它类通过对象调用。
范例:属性不封装情况下的问题
class Person{// 定义一个类
// 成员属性
String name ;
int age ;
// 操作方法
public void tell(){
System.out.println("姓名:" + name + " " + "年龄:" + age ) ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
Person per = new Person() ; // 申明并实例化
per.name = "张三" ; // 在类外部修改属性
per.age = 80 ; // 在类外部修改属性
per.tell() ; // 操作方法调用
}
}
此时在类Person中的属性name与age并没有进行封装处理,这样外部就可以直接调用的,但是有可能所设置的数据是错误的数据,如age = -18;
利用private关键字对属性进行封装处理。
范例:对属性进行封装
class Person{// 定义一个类
// 成员属性
private String name ;
private int age ;
// 操作方法
public void tell(){
System.out.println("姓名:" + name + " " + "年龄:" + age ) ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
Person per = new Person() ; // 申明并实例化
per.name = "张三" ; // 在类外部修改属性
per.age = 80 ; // 在类外部修改属性
per.tell() ; // 操作方法调用
}
}
属性封装后对外部不可见,但对内部是可见的。要从外部访问属性,则在Java开发中提供如下要求:
|- 设置属性,public void setName(name);
|- 获取属性,public String getName()。
范例:实现封装
class Person{// 定义一个类
// 成员属性
private String name ;
private int age ;
// 操作方法
public void setName(String n){
name = n ;
}
public void setAge(int a){
if (a>0){
age = a ;
}
}
public String getName(){
return name ;
}
public int getAge(){
return age ;
}
public void tell(){
System.out.println("姓名:" + name + " " + "年龄:" + age ) ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
Person per = new Person() ; // 申明并实例化
per.setName("张三") ;
per.setAge(-18) ;
per.tell() ; // 操作方法调用
}
}
在以后写任何类得时候所有属性都必须用private封装(98%)。属性需要访问,必须提供setter、getter方法。
现在的程序使用类是一般按照如下步骤进行:
范例:传统调用
class Person{// 定义一个类
// 成员属性
private String name ;
private int age ;
// 操作方法
public void setName(String n){
name = n ;
}
public void setAge(int a){
if (a>0){
age = a ;
}
}
public String getName(){
return name ;
}
public int getAge(){
return age ;
}
public void tell(){
System.out.println("姓名:" + name + " " + "年龄:" + age ) ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
// 1. 对象的初始化准备
Person per = new Person() ; // 申明并实例化
per.setName("张三") ;
per.setAge(-18) ;
// 2. 对象的使用
per.tell() ; // 操作方法调用
}
}
如果类中属性太多,不适合用这种。Java提供构造方法实现实例化对象中的属性初始化处理。构造方法的定义要求如下:
范例:定义构造方法
class Person{// 定义一个类
// 成员属性
private String name ;
private int age ;
// 构造方法,无返回值定义
public Person(String n , int a){
name = n ; // 为类中属性赋值(初始化)
age = a ; // 为类中属性赋值(初始化)
}
// 操作方法
public void tell(){
System.out.println("姓名:" + name + " " + "年龄:" + age ) ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
// 1. 对象的初始化准备
Person per = new Person("张三", 18) ; // 申明并实例化
// 2. 对象的使用
per.tell() ; // 操作方法调用
}
}
当前的实例化格式与之前的实例化格式进行一个比较:
|- ”1)Person“ :主要是定义对象所属类型,类型决定你可以调用的方法;
|-”2)per“ : 实例化对象的名称
|-”3)new“:开辟一块堆内存空间
|-”4)Person(”张三“, 18)“ :调用有参构造; ”4)Person()“:调用无参构造
在Java程序中考虑到程序的完整性,所有类都会提供构造方法。如果类没有定义构造方法,那么一定会提供一个默认的无参的构造方法,默认值为参数类型的默认值,它是在程序编译的时候自动创建的。如果明确有构造方法时,那么默认的构造方法不会被自动创建,也无法声明无参对象。
结论:一个类至少存在一个构造方法。
构造方法上不能设置返回值类型的原因?为什么不使用void定义?
分析:程序编译器根据代码结构进行编译处理的,执行也是根据代码结构执行的。如果在构造方法上使用void,就和普通方法结构完全相同的。普通方法与构造方法最大的区别:构造方法在类对象实例化的时候调用的,而普通方法是类对象实例化产生之后调用的。
构造方法是一种方法,方法都具有重载的特点,而构造方法在重载时只需要考虑参数的类型与个数即可。
范例:构造方法重载
class Person{// 定义一个类
// 成员属性
private String name ;
private int age ;
// 构造方法
public Person(){
name = "无名氏" ;
age = -1 ;
}
public Person(String n){
name = n ;
}
public Person(String n , int a){
name = n ; // 为类中属性赋值(初始化)
age = a ; // 为类中属性赋值(初始化)
}
public void tell(){
System.out.println("姓名:" + name + " " + "年龄:" + age ) ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
// 1. 对象的初始化准备
Person per = new Person("张三") ; // 申明并实例化
// 2. 对象的使用
per.tell() ; // 操作方法调用
}
}
在进行多个构造方法定义时,按照参数个数升序排列。
经过分析,构造方法可以进行属性设置,setter也可以进行属性设置。构造方法是对象实例化的时候属性设置初始化内容,而setter拥有属性数据初始化的功能,还具有修改属性数据的功能。
范例:使用setter修改数据
class Person{// 定义一个类
// 成员属性
private String name ;
private int age ;
// 构造方法
public Person(){
name = "无名氏" ;
age = -1 ;
}
public Person(String n){
name = n ;
}
public Person(String n , int a){
name = n ; // 为类中属性赋值(初始化)
age = a ; // 为类中属性赋值(初始化)
}
public void setAge(int a){
age = a ;
}
public void tell(){
System.out.println("姓名:" + name + " " + "年龄:" + age ) ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
// 1. 对象的初始化准备
Person per = new Person("张三") ; // 申明并实例化
// 2. 对象的使用
per.setAge(18);
per.tell() ; // 操作方法调用
}
}
经过分析过后发现,利用构造方法可以传递属性数据于是进一步分析对象的产生格式:
如果这个时候,我们进行实例化对象来进行类的操作也是可以的,而这种形式的对象由于没有名字,称为匿名对象。
范例:观察匿名对象
public class JavaDemo{ // 主类
public static void main( String arges[] ){
new Person("张三", 10).tell() ;
}
}
此时依然通过对象进行类tell()方法的调用,由于此时没有任何的引用名称,因此使用一次过后就将成为垃圾,所有的垃圾就将被GC进行回收与释放。
此时程序已经有了构造方法,那么通过一个程序进行构造方法的内粗你分析:
范例:编写一个分析程序
class Message{
private String title ;
public Message(String t){
title = t ;
}
public void setTitle(String t){ //具有修改数据功能
title = t ;
}
public String getTitle(){
return title ;
}
}
class Person{// 定义一个类
// 成员属性
private String name ;
private int age ;
// 构造方法
public Person(Message msg, int a){
name = msg.getTitle() ; // 为类中属性赋值(初始化)
age = a ; // 为类中属性赋值(初始化)
}
public Message getInfo(){
return new Message(name + " : " + age) ;
}
public void tell(){
System.out.println("姓名:" + name + " " + "年龄:" + age ) ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
Message msg = new Message("mldn") ;
Person per = new Person(msg, 20) ;
msg = per.getInfo() ;
System.out.println(msg.getTitle()) ;
}
}
通过此程序,进行内存分析
只要是方法都可以传递任意的数据类型(基本数据类型、引用数据类型)
this是Java中比较复杂的关键字,因为在使用形式上决定了它的灵活性,使用this可以实现三类结构的描述:
当前类中的属性:this.属性;
当前类中的方法:构造方法:this()、普通方法:this.方法名称();
描述当前对象;
通过前面分析知道,利用构造方法或setter方法都可以进行类中属性的赋值,但是在进行赋值的时候,之前采用如下的形式:
范例:构造方法赋值
class Person{// 定义一个类
// 成员属性
private String name ;
private int age ;
// 构造方法
public Person(String n, int a){
name = n ; // 为类中属性赋值(初始化)
age = a ; // 为类中属性赋值(初始化)
}
// setter getter 略
public void tell(){
System.out.println("姓名:" + name + " " + "年龄:" + age ) ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
Person per = new Person("王五", 38 ) ;
per.tell() ;
}
}
可以看到构造方法中的参数n与a,是给name与age赋值,此时发现n与a 的参数名称不好。显然写成name与age更好,但这样无法进行属性的正确设置。
public Person(String name, int age){
name = name ; // 为类中属性赋值(初始化)
age = age ; // 为类中属性赋值(初始化)
}
在Java中“{}”是作为结构体的边界符,在程序里进行变量(参数、属性变量)使用时,都以大括号作为查找边界。当前name与age的查找边界是边界中,以就近原则,构造方法并没有访问类中属性,所以为了区分在类中属性与参数,往往在类中属性前加上this。
class Person{// 定义一个类
// 成员属性
private String name ;
private int age ;
// 构造方法
public Person(String name, int age){
this.name = name ; // 为类中属性赋值(初始化)
this.age = age ; // 为类中属性赋值(初始化)
}
// setter getter 略
public void tell(){
System.out.println("姓名:" + this.name + " " + "年龄:" + this.age ) ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
Person per = new Person("王五", 38 ) ;
per.tell() ;
}
}
在以后开发中,访问本类中属性一定要加this。
this除了调用本类中属性,也可以调用本类方法,但调用本类方法必须考虑构造方法与普通方法。
范例:调用类中普通方法
class Person{// 定义一个类
// 成员属性
private String name ;
private int age ;
// 构造方法
public Person(String name, int age){
this.name = name ; // 为类中属性赋值(初始化)
this.age = age ; // 为类中属性赋值(初始化)
}
// setter getter
public void setName(String name){
this.name = name ;
}
public void setAge(int age){
this.age = age ;
}
public String getName(){
return this.name ;
}
public int getAge(){
return this.age ;
}
//普通方法
public void tell(){
System.out.println("姓名:" + this.getName() + " " + "年龄:" + this.getAge() ) ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
Person per = new Person("王五", 38 ) ;
per.tell() ;
}
}
除了普通的方法调用,还需要构造方法的调用。对于构造方法的调用,必须放在构造方法中执行。现在假设类中一共有三种构造方法,不管调用那种构造方法,都执行一个输出语句“一个Person类的实例化对象”。
范例:传统做法实现
class Person{// 定义一个类
// 成员属性
private String name ;
private int age ;
// 构造方法
public Person(){
System.out.println("*** 一个新的操作对象实例化 ***") ;
}
public Person(String){
this.name = name ;
System.out.println("*** 一个新的操作对象实例化 ***") ;
}
public Person(String name, int age){
this.name = name ; // 为类中属性赋值(初始化)
this.age = age ; // 为类中属性赋值(初始化)
System.out.println("*** 一个新的操作对象实例化 ***") ;
}
// setter getter 略
//普通方法
public void tell(){
System.out.println("姓名:" + this.name + " " + "年龄:" + this.age ) ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
Person per = new Person("王五", 38 ) ;
per.tell() ;
}
}
如果“System.out.println("*** 一个新的操作对象实例化 ***") ”这句话是多行代码,则显然是代码重复问题。如果评价代码的好坏标准:
范例:利用this构造优化
class Person{// 定义一个类
// 成员属性
private String name ;
private int age ;
// 构造方法
public Person(){
System.out.println("*** 一个新的操作对象实例化 ***") ;
}
public Person(String name){
this() ; // 调用无参构造
this.name = name ;
}
public Person(String name, int age){
this(name) ; // 调用单参数构造
this.age = age ;
}
// setter getter 略
//普通方法
public void tell(){
System.out.println("姓名:" + this.name + " " + "年龄:" + this.age ) ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
Person per = new Person("王五", 38 ) ;
per.tell() ;
}
}
这种才是设计结构合理的。
对于本类构造方法的调用必须注意以下几个问题:
死循环的提示:告诉用户,你出现了构造方法的递归调用。
范例:构造方法互相调用
现在定义一个描述员工信息的程序类,该类提供有:编号、姓名、部门、工资,在这个类之中提供四个构造方法:
【无参构造】编号1000,姓名定义为无名氏;
【单参构造】传递编号,姓名定义为“新员工”,部门定义为“未定”;
【三参构造】传递编号、姓名、部门,工资为2500。00;
【四参构造】所有信息都传递;
范例:代码的初期实现
class Emp{
private long empno ; //员工编号
private String ename ; //员工姓名
private String dept ; //员工部门
private double salary ; //员工工资
public Emp(){
this.empno = 1000 ;
this.ename = "无名氏" ;
}
public Emp(long empno){
this.empno = empno ;
this.ename = "新员工" ;
this.dept = "未定" ;
}
public Emp(long empno, String ename, String dept){
this.empno = empno ;
this.ename = ename ;
this.dept = dept ;
this.salary = 2500.00 ;
}
public Emp(long empno, String ename, String dept, double salary){
this.empno = empno ;
this.ename = ename ;
this.dept = dept ;
this.salary = salary ;
}
//setter getter略
public String getInfo(){
return "员工编号:" + this.empno +"\n"+
" 姓名:" + this.ename +"\n"+
" 部门:" + this.dept +"\n"+
" 工资:" + this.salary ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
Emp emp = new Emp(7369, "SMTh", "财务部", 6500);
System.out.println( emp.getInfo() ) ;
}
}
此时发现代码有重复,此时可以用this优化:
class Emp{
private long empno ; //员工编号
private String ename ; //员工姓名
private String dept ; //员工部门
private double salary ; //员工工资
public Emp(){
this(1000, "无名氏", null, 0.0) ;
}
public Emp(long empno){
this(empno, "新员工", "未定", 0.0) ;
}
public Emp(long empno, String ename, String dept){
this(empno, ename, dept, 2500.00) ;
}
public Emp(long empno, String ename, String dept, double salary){
this.empno = empno ;
this.ename = ename ;
this.dept = dept ;
this.salary = salary ;
}
//setter getter略
public String getInfo(){
return "员工编号:" + this.empno +"\n"+
" 姓名:" + this.ename +"\n"+
" 部门:" + this.dept +"\n"+
" 工资:" + this.salary ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
Emp emp = new Emp(7369, "SMTh", "财务部", 6500);
System.out.println( emp.getInfo() ) ;
}
}
代码的任何位置上,都可能出现重复,消除重复的方法是先期学习的重点
在以后的开发与设计中,简单Java类都将成为最重要的一个组成部分,慢慢的接触到正规的设计过后,简单Java类无处不在,并且有可能会产生一系列的变化。所谓简单Java类,可以是描述某类信息的程序类,例如:描述一个人、描述一本书、描述一个部门、描述一个雇员,并且在这个类中无复杂的逻辑操作,只作为信息存储的媒介。
对于简单的Java类而言,其核心开发结构如下:
class Dept{ // 类名称可以明确描述出某类事物
private long deptno ;
private String dname ;
private String loc ;
public Dept(){} // 提供无参构造
public Dept(long deptno, String dname, String loc){
this.deptno = deptno ;
this.dname = dname ;
this.loc = loc ;
}
public String getInfo(){
return "【部门信息】 部门编号:" + this.deptno +
"、部门名称:" + this.dname +
"、部门位置:" + this.loc ;
}
public void setDeptno(long deptno){
this.deptno = deptno ;
}
public void setDname(String dname){
this.dname = dname ;
}
public void setLoc(String loc){
this.loc = loc ;
}
public long getDept(){
return this.deptno ;
}
public String getDname(){
return this.dname ;
}
public String getLoc(){
return this.loc ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
Dept dept = new Dept(001, "平头哥", "天堂");
System.out.println(dept.getInfo()) ;
}
}
这中简单Java融合了到现在所学的内容,如数据划分、类的定义、private封装、构造方法、方法定义、对象实例化、this。
static关键字,主要用来定义属性与方法。
在一个类中所有的属性被定义,内容都交由堆内存空间进行保存。
范例:观察传统情况
class Person{ // 创建所有同一个国家的类
private String name ;
private int age ;
String country = "中华民国"; //国家,为了说明暂时不封装
public Person(){}
public Person(String name, int age){
this.name = name ;
this.age = age ;
}
// setter getter略
public String getInfo(){
return "姓名:" + this.name +
"、年龄:" + this.age +
"、国家" + this.country ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
Person perA = new Person("张三", 10);
Person perB = new Person("李四", 10);
Person perC = new Person("王五", 11);
System.out.println(perA.getInfo()) ;
System.out.println(perB.getInfo()) ;
System.out.println(perC.getInfo()) ;
}
}
为了观察程序,进行内存分析
每一个对象保存各自的属性,国家解放了,变成“中华人民共和国”,如果有五千个对象。每一个对象都有自己的属性,就有重复保存数据和修改不方便的问题。传统开发中没有公共概念,最好的解决方案是将country修改为公共属性,就使用static关键字。形式如下:
范例:修改Person类定义使用static,定义公共属性
class Person{ // 创建所有同一个国家的类
private String name ;
private int age ;
static String country = "中华民国"; //国家,为了说明暂时不封装
public Person(){}
public Person(String name, int age){
this.name = name ;
this.age = age ;
}
// setter getter略
public String getInfo(){
return "姓名:" + this.name +
"、年龄:" + this.age +
"、国家" + this.country ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
Person perA = new Person("张三", 10);
Person perB = new Person("李四", 10);
Person perC = new Person("王五", 11);
perA.country = "中华人民共和国" ;
System.out.println(perA.getInfo()) ;
System.out.println(perB.getInfo()) ;
System.out.println(perC.getInfo()) ;
}
}
此时会发现所有的country都发生了改变,所以这是一个公共属性,而此时的内存关系图如下:
但是对于static数据的访问,虽然可以通过对象访问,但需要通过所有对象的代表(类)访问,所以static属性可以通过类名称直接进行调用。
Person.country = "中华人民共和国" ;
但同时static属性虽然定义在类中,但不受对象实例化的控制,即static属性可以在没有类对象实例化的时候使用。
范例:不产生实例化对象调用static属性
public class JavaDemo{ // 主类
public static void main( String arges[] ){
// 没有实例化对象
System.out.println(Person.country) ;
Person.country = "中华人民共和国" ;
// 实例化对象
Person per = new Person("张三", 10) ;
System.out.println(per.getInfo()) ;
}
}
以后再进行类设计的时候,首选非static属性(95%)。考虑到公共信息存储时才考虑static属性,非static属性必须在实例化对象之后才能使用,static在没有实例化直接通过类名称就可以调用。
static也可以进行方法的定义,static方法的特点在于,其可以直接使用类名称,在没有实例化对象的时候可以使用。
范例:定义static方法
class Person{ // 创建所有同一个国家的类
private String name ;
private int age ;
private static String country = "中华民国"; //国家,为了说明暂时不封装
public Person(){}
public Person(String name, int age){
this.name = name ;
this.age = age ;
}
// setter getter略
public static void setCountry(String c){ // static定义方法
country = c ;
}
public String getInfo(){
return "姓名:" + this.name +
"、年龄:" + this.age +
"、国家" + this.country ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
// 没有实例化对象
Person.setCountry("中华人民共和国") ;
// 实例化对象
Person per = new Person("张三", 10) ;
System.out.println(per.getInfo()) ;
}
}
这个时候对于程序而言,现在可以将方法两种:static方法、非static方法。这两种方法之间,在调用上就有了限制:
static定义的属性与方法可以在没有进行实例化对象的情况下使用,而非static方法与属性只能在实例化对象对象的情况下才能使用。
如果可以理解这个限制,那么对于方法定义可以得到新的结论:在最早讲解方法定义的时候强调过:“当前定义的方法,都是在主类中定义的,并且由主类调用”。
public class JavaDemo{ // 主类
public static void main( String arges[] ){
new JavaDemo().print() ; // 通过对象来调用static
}
public static void print(){
System.out.println("这是一个测试") ;
}
}
static定义的方法或者属性,都不是代码设计之初你需要考虑的内容,只有在回避实例化调用并且在描述公共属性的时候,才会考虑static定义的方法或属性。
为了加强理解,用两个程序来进行static应用提示。
范例:编写一个程序类,这个类可以实现实例化对象个数的统计,每一次创建一个新的实例化对象都可以实现一个统计操作。
分析:此时可以单独创建一个static属性,因为所有的对象都在共享同一个属性,那么构造方法中实现数据的统计处理:
class Book{
private String title ;
private static int count = 0 ;
public Book(String title){
this.title = title;
count ++ ;
System.out.println("第" + count + "新的图书创建出来") ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
new Book("Java") ; new Book("JSP") ; new Book("SPRING") ;
}
}
范例:实现属性的自动命名处理,没有传递title,使用“NONETITLE-编号”定义
class Book{
private String title ;
private static int count = 0 ;
public Book(){
this("NOTITLE - " + count++) ;
}
public Book(String title){
this.title = title;
count ++ ;
}
// setter getter 略
public String getTitle(){
return this.title ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
System.out.println(new Book("Java").getTitle()) ;
System.out.println(new Book("JSP").getTitle()) ;
System.out.println(new Book("Spring").getTitle()) ;
System.out.println(new Book().getTitle()) ;//无参
System.out.println(new Book().getTitle()) ;//无参
}
}
这样处理的好处是,避免了在没有设置title属性时内容为null的重复问题。
在程序之中,使用大括号定义的结构,称之为代码块,而根据代码块出现位置以及定义的关键字的不同,代码块可以分为:普通代码块、构造块、静态块、同步代码块,其中对于同步代码块对于多线程讲解。
普通代码块的特点是定义在一个方法之中代码块。
范例:观察一个程序
public class JavaDemo{ // 主类
public static void main( String arges[] ){
if (true){
int x = 10 ; // 局部变量
System.out.println("x = " + x) ;
}
int x = 100 ;// 全局变量 相对而言
System.out.println("x = " + x) ;
}
}
按照Java标准规定,相同名称的变量不能在同一个方法之中存在的,由于此时由不同的分界描述。要定义普通代码块,直接将if 语句取消即可。
public class JavaDemo{ // 主类
public static void main( String arges[] ){
{//普通代码块
int x = 10 ; // 局部变量
System.out.println("x = " + x) ;
}
int x = 100 ;// 全局变量 相对而言
System.out.println("x = " + x) ;
}
}
普通代码块的最大特征,可以在一个方法之中可以进行一些结构的拆分,以防止相同变量名称所带来的互相影响。
构造块是定义在一个类中。
范例:观察构造块
class Person{
public Person(){
System.out.println("【构造方法】Person类构造方法执行");
}
{
System.out.println("【构造块】Person构造块执行");
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
new Person() ;
new Person() ;
new Person() ;
}
}
可以看出构造块先于构造方法执行,并且每一次实例化新对象的时候都会调用构造块中的代码。
静态代码块主要是指使用static关键字定义的代码块,静态块的定义需要考虑两种情况:一种是主类中定义静态块、非主类中定义静态块。
范例:在非主类中进行静态块的定义
class Person{
public Person(){
System.out.println("【构造方法】Person类构造方法执行");
}
static{//一般用来static属性初始化的
System.out.println("【静态块】静态块执行") ;
}
{
System.out.println("【构造块】Person构造块执行");
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
new Person() ;
new Person() ;
new Person() ;
}
}
此时发现静态块优先于构造块执行,并且无论多少个对象出现,静态代码块只会执行一次。静态代码块主要是为了类中static属性初始化。
范例:观察静态属性的初始化
class Message{
public static String getCountry(){
//该信息内容可能来自于网络或其它服务器
return "中华人民共和国" ;
}
}
class Person{
private static String country ;
static{// 可能有多个语句执行
country = new Message().getCountry() ; // 编写一部分代码
System.out.println(country) ;
}
}
public class JavaDemo{ // 主类
public static void main( String arges[] ){
new Person() ;
}
}
对于静态代码块还需要考虑在主类中定义的形式。
范例:在主类中进行静态代码块的定义
public class JavaDemo{ // 主类
static{
System.out.println("****** 程序初始化 *****") ;
}
public static void main( String arges[] ){
System.out.println("这是一个测试程序");
}
}
静态代码块优先于主方法先执行。