且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)

更新时间:2022-08-18 18:49:06

上季内容回顾:
1、内部类 
2、对象数组的使用
本季主要知识点:
本季要点:static关键字的使用。
本季讲解了Java中static关键字的使用以及单态设计模式。
static的使用
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
设计一个类:Person类,有name、age、city三个属性
class Person 

  //为了方便,属性暂时不封装 
  String name ; 
  int age ; 
  //所在城市默认为南京 
  String city = "南京" ; 
  public Person(String name,int age) 
  { 
    this.name = name ; 
    this.age = age ; 
  } 
  public String getInfo() 
  { 
    return "姓名:"+name+",年龄:"+age+",城市:"+city ;    
  } 
}; 
public class Demo01 

  public static void main(String args[]) 
  { 
    Person p1 = new Person("张三",30) ; 
    Person p2 = new Person("李四",31) ; 
    Person p3 = new Person("王五",32) ; 
    System.out.println(p1.getInfo()) ; 
    System.out.println(p2.getInfo()) ; 
    System.out.println(p3.getInfo()) ; 
  } 
};
 
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
先把内存结构图画出来
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
仔细观察,这三个对象的city属性的值都是南京,就好比说我现在所产生的人都是南京人。 
那么这样的设计就会存在问题: 
1、 属性的内容重复存在,所有对象都有此属性 
2、 如果现在假设南京变为北京了,假设已经产生了100000个对象,则此时如果要修改其所在城市属性的内容,则肯定要修改100000遍
解决方法: 
· 所有的对象都共同指向同一个city属性是***的,有一个对象将city属性修改了,则其他的也会影响。此时就需要使用static标识city属性了。
class Person 

  //为了方便,属性暂时不封装 
  String name ; 
  int age ; 
  //所在城市默认为南京 
  static String city = "南京" ; 
  public Person(String name,int age) 
  { 
    this.name = name ; 
    this.age = age ; 
  } 
  public String getInfo() 
  { 
    return "姓名:"+name+",年龄:"+age+",城市:"+city ;    
  } 
}; 
public class Demo02 

  public static void main(String args[]) 
  { 
    Person p1 = new Person("张三",30) ; 
    Person p2 = new Person("李四",31) ; 
    Person p3 = new Person("王五",32) ; 
    System.out.println(p1.getInfo()) ; 
    System.out.println(p2.getInfo()) ; 
    System.out.println(p3.getInfo()) ; 
  } 
};
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
再来看下内存结构
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
加入static 声明之后所有的static 类型的属性都保存在了全局数据区之中,所有的对象都共同拥有同一个city属性了。
class Person 

  //为了方便,属性暂时不封装 
  String name ; 
  int age ; 
  //所在城市默认为南京 
  static String city = "南京" ; 
  public Person(String name,int age) 
  { 
    this.name = name ; 
    this.age = age ; 
  } 
  public String getInfo() 
  { 
    return "姓名:"+name+",年龄:"+age+",城市:"+city ;    
  } 
}; 
public class Demo03 

  public static void main(String args[]) 
  { 
    Person p1 = new Person("张三",30) ; 
    Person p2 = new Person("李四",31) ; 
    Person p3 = new Person("王五",32) ; 
    System.out.println("----------修改city属性之前----------") ; 
    System.out.println(p1.getInfo()) ; 
    System.out.println(p2.getInfo()) ; 
    System.out.println(p3.getInfo()) ; 
    System.out.println("----------修改city属性之后----------") ; 
    //通过一个对象修改city属性值 
    p1.city = "北京" ; 
    System.out.println(p1.getInfo()) ; 
    System.out.println(p2.getInfo()) ; 
    System.out.println(p3.getInfo()) ; 
  } 
};
验证下效果
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
一个对象修改static属性之后,其他的所有对象都可以更改了。 
但是以上的程序有一些不妥,一个城市改变是应该由某个人(某个对象)去做的吗?应该是由对象的集合体去做,对象的集合体就是类,所以一般在访问static属性的时候都采用如下格式: 
· 类名称.static属性      这是最合理的。
class Person 

  //为了方便,属性暂时不封装 
  String name ; 
  int age ; 
  //所在城市默认为南京 
  static String city = "南京" ; 
  public Person(String name,int age) 
  { 
    this.name = name ; 
    this.age = age ; 
  } 
  public String getInfo() 
  { 
    return "姓名:"+name+",年龄:"+age+",城市:"+city ;    
  } 
}; 
public class Demo04 

  public static void main(String args[]) 
  { 
    Person p1 = new Person("张三",30) ; 
    Person p2 = new Person("李四",31) ; 
    Person p3 = new Person("王五",32) ; 
    System.out.println("----------修改city属性之前----------") ; 
    System.out.println(p1.getInfo()) ; 
    System.out.println(p2.getInfo()) ; 
    System.out.println(p3.getInfo()) ; 
    System.out.println("----------修改city属性之后----------") ; 
    //通过一个对象修改city属性值 
    //***是通过类名称去修改static属性,即static属性可以被类名称直接访问 
    Person.city = "北京" ; 
    System.out.println(p1.getInfo()) ; 
    System.out.println(p2.getInfo()) ; 
    System.out.println(p3.getInfo()) ; 
  } 
};
验证一下
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
有些时候也有人把static类型的属性称为类属性(类变量),因为可以直接由类名称进行访问。既然static 可以声明属性,则static 也可以声明方法,使用static 声明的方法称为类方法,也可以由类名称直接调用。
class Person 

  //为了方便,属性暂时不封装 
  private String name ; 
  private int age ; 
  //所在城市默认为南京 
  private static String city = "南京" ; 
  //只编写一个可以修改city的方法 
  public static void setCity(String city) 
  { 
    this.city = city ; 
  } 
  public Person(String name,int age) 
  { 
    this.name = name ; 
    this.age = age ; 
  } 
  public String getInfo() 
  { 
    return "姓名:"+name+",年龄:"+age+",城市:"+city ;    
  } 
}; 
public class Demo05 

  public static void main(String args[]) 
  { 
    Person p1 = new Person("张三",30) ; 
    Person p2 = new Person("李四",31) ; 
    Person p3 = new Person("王五",32) ; 
    System.out.println("----------修改city属性之前----------") ; 
    System.out.println(p1.getInfo()) ; 
    System.out.println(p2.getInfo()) ; 
    System.out.println(p3.getInfo()) ; 
    System.out.println("----------修改city属性之后----------") ; 
    //通过一个对象修改city属性值 
    //***是通过类名称去修改static属性,即static属性可以被类名称直接访问 
    //Person.city = "北京" ; 
    Person.setCity("无锡") ; 
    System.out.println(p1.getInfo()) ; 
    System.out.println(p2.getInfo()) ; 
    System.out.println(p3.getInfo()) ; 
  } 
};
这样验证下有没问题哈~
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
class Person 

  //为了方便,属性暂时不封装 
  private String name ; 
  private int age ; 
  //所在城市默认为南京 
  private static String city = "南京" ; 
  //只编写一个可以修改city的方法 
  public static void setCity(String c) 
  { 
    //this是表示当前对象,static属性是绝对不能用this表示滴~ 
    //因为static属性不绝对属于某一个对象 
    //this.city = city ;     ---->这是错误滴哈~~~ 
    city = c ; 
  } 
  public Person(String name,int age) 
  { 
    this.name = name ; 
    this.age = age ; 
  } 
  public String getInfo() 
  { 
    return "姓名:"+name+",年龄:"+age+",城市:"+city ;    
  } 
}; 
public class Demo06 

  public static void main(String args[]) 
  { 
    Person p1 = new Person("张三",30) ; 
    Person p2 = new Person("李四",31) ; 
    Person p3 = new Person("王五",32) ; 
    System.out.println("----------修改city属性之前----------") ; 
    System.out.println(p1.getInfo()) ; 
    System.out.println(p2.getInfo()) ; 
    System.out.println(p3.getInfo()) ; 
    System.out.println("----------修改city属性之后----------") ; 
    //通过一个对象修改city属性值 
    //***是通过类名称去修改static属性,即static属性可以被类名称直接访问 
    //Person.city = "北京" ; 
    // 使用static声明的方法可以直接使用类名称调用 
    Person.setCity("无锡") ; 
    System.out.println(p1.getInfo()) ; 
    System.out.println(p2.getInfo()) ; 
    System.out.println(p3.getInfo()) ; 
  } 
};
看下程序效果
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
Static使用的限制: 
1、使用static声明的方法能否访问static声明的属性呢?允许的
class Demo07 

  static String name = "Michael" ; 
  public static void fun() 
  { 
    System.out.println(name); 
  } 
};
编译通过哈~
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
2、使用static方法能否访问非static声明的属性呢?不允许的
class Demo08 

  String name = "Michael" ; 
  public static void fun() 
  { 
    System.out.println(name); 
  } 
};
编译不能通过,说明是不允许滴~~~
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
3、使用非static方法能否访问static声明的属性呢?允许的
class Demo09 

  static String name = "Michael" ; 
  public void fun() 
  { 
    System.out.println(name); 
  } 
};
编译通过,说明是允许滴
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
4、使用非static能否访问非static声明的属性呢?允许的
class Demo10 

  String name = "Michael" ; 
  public void fun() 
  { 
    System.out.println(name); 
  } 
};
这个编译也通过,说明也是允许滴
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
Main方法调用其他方法的问题: 
· 之前讲解方法的时候给了一个限制:如果一个方法想被主方法直接调用,则此方法声明时必须加上public static才可以。
public class Demo11 

  public static void main(String args[]) 
  { 
    fun(); 
  } 
  public static void fun() 
  { 
    System.out.println("http://redking.blog.51cto.com"); 
  } 
};
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
如果我们在fun()方法前没有加上static滴话,我们看下会报什么错误
public class Demo12 

  public static void main(String args[]) 
  { 
    fun(); 
  } 
  public void fun() 
  { 
    System.out.println("http://redking.blog.51cto.com"); 
  } 
};
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
注意:static类型的方法只能调用static类型的方法
public class Demo13 

  //static类型的方法只能调用static类型的方法 
  public static void main(String args[]) 
  { 
    //如果fun()方法前不加static,则只能这样调用 
    new Demo13().fun(); 
  } 
  public void fun() 
  { 
    System.out.println("http://redking.blog.51cto.com"); 
  } 
};
程序执行正常
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
主方法的含义:public static void main(String args[])
理解main方法
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
String args[]:表示运行时的参数 ==> 表示String类型的对象数组
public class Demo14 

  public static void main(String args[]) 
  { 
    for (int i=0;i<args.length ;i++ ) 
    { 
      System.out.println(args[i]); 
    } 
  } 
};
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
我们发现没有内容输出哈,那我们在javac Demo14.java后面跟上几个数据哈,以空格分开
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
所有的参数会在命令后面加入空格的形式出现。
我们做个练习哈~
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
public class Demo15 

  public static void main(String args[]) 
  { 
    //必须输入两个参数 
    if (args.length!=2) 
    { 
      //表示不是两个参数,则要退出系统 
      System.out.println("运行参数不正确,正确的格式为:"); 
      System.out.println("java Demo15 用户名 密码"); 
      System.exit(1); 
    } 
    //之后的程序就表示肯定已经成功的输入了用户名和密码哈~~~ 
    String userName = args[0]; 
    String userPasswd = args[1]; 
    //判断用户名是否为abc,密码是否为123哈~~~ 
    //为什么现在要用字符串.equals去判断而不是用username调用equals哈~ 
    //这样可以有效滴避免NullPointerException 
    if ("abc".equals(userName)&&"123".equals(userPasswd)) 
    { 
      System.out.println("欢迎访问哈~~~"); 
    } 
    else 
    { 
      System.out.println("用户名和密码不正确哈~~~"); 
    } 
  } 
};
验证一下程序
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
解释:为什么要把字符串放在前面比较
public class Demo16 

  public static void main(String args[]) 
  { 
    String str = "http://redking.blog.51cto.com"; 
    System.out.println(str.equals("http://redking.blog.51cto.com")); 
  } 
};
程序执行下,看下有没问题哈~
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
结果为true,没有出错哈,但是大家知道str是引用数据类型哈,如果str值为null空的话,就会出现问题。
public class Demo17 

  public static void main(String args[]) 
  { 
    String str = null
    System.out.println(str.equals("http://redking.blog.51cto.com")); 
  } 
};
程序提示NullPointerException错误哈~
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
那我们现在把str和字符串交换下位置看下效果
public class Demo18 

  public static void main(String args[]) 
  { 
    String str = null
    System.out.println("http://redking.blog.51cto.com".equals(str)); 
  } 
};
字符串http://redking.blog.51cto.com 永远不会为空哈,所以不会报NullPointerException错误哈~
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
因此我们在开发中经常把字符串写在前面,然后调用equals方法进行比较哈,这样可以避免出现错误~~
Static的基本概念已经掌握清楚了,那么还可以在以下一种情况下使用: 
· 如果现在想统计一个类到底产生了多少个对象时,就需要使用static关键字了,因为所有的对象都只拥有同一个static类型的属性。
class Demo 

  private static int count = 1; 
  public Demo() 
  { 
    System.out.println("产生了"+(count++)+"个对象~~~~");    
  } 

public class Demo19 

  public static void main(String args[]) 
  { 
    new Demo(); 
    new Demo(); 
    new Demo(); 
    new Demo(); 
    new Demo(); 
  } 
};
这个程序只是简单测试一下哈,因为垃圾回收时间不确定,呵呵
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
 
代码块:java中存在四种代码块: 
1、普通代码块:是直接写在各个方法中的代码块
public class Demo20 

  public static void main(String args[]) 
  { 
    //普通代码块 
    { 
    int i = 10; 
    System.out.println(i); 
    } 
    //在代码块里定义的变量实际上是一个局部变量 
    int i = 99; 
    System.out.println(i); 
  } 
};
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
2、构造块:是直接写在类中的代码块,在实例化对象的时候调用,而且每实例化一个对象就要调用一次构造块(多次调用)。构造块优先于构造方法执行。
class Demo 

  { 
    //构造块 
    System.out.println("Demo类中的构造块~~~"); 
  } 

public class Demo21 

  public static void main(String args[]) 
  { 
    new Demo(); 
    new Demo(); 
    new Demo(); 
    new Demo(); 
    new Demo(); 
  } 
};
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
我们来验证一下构造块优先于构造方法执行
class Demo 

  { 
    //构造块 
    System.out.println("Demo类中的构造块~~~"); 
  } 
  Demo() 
  { 
    System.out.println("Demo类中的构造方法~~~"); 
  } 

public class Demo22 

  public static void main(String args[]) 
  { 
    new Demo(); 
    new Demo(); 
    new Demo(); 
    new Demo(); 
    new Demo(); 
  } 
};
我们看到构造块优先于构造方法执行哈~
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
3、静态块:是使用static声明的代码块,静态块只调用一次,而且优先于构造块执行。 
作用:为static类型的属性初始化
class Demo 

  { 
    //构造块 
    System.out.println("Demo类中的构造块~~~"); 
  } 
  Demo() 
  { 
    System.out.println("Demo类中的构造方法~~~"); 
  } 
  //静态块 
  static 
  { 
    System.out.println("Demo类中的静态块~~~"); 
  } 

public class Demo23 

  public static void main(String args[]) 
  { 
    new Demo(); 
    new Demo(); 
    new Demo(); 
    new Demo(); 
    new Demo(); 
  } 
};
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
在主类中写的静态块要优先于main方法执行
class Demo 

  { 
    //构造块 
    System.out.println("Demo类中的构造块~~~"); 
  } 
  Demo() 
  { 
    System.out.println("Demo类中的构造方法~~~"); 
  } 
  //静态块 
  static 
  { 
    System.out.println("Demo类中的静态块~~~"); 
  } 

public class Demo24 

  //在主类中写的静态块要优先于main方法执行 
  static
    System.out.println("##################"); 
  } 
  public static void main(String args[]) 
  { 
    new Demo(); 
    new Demo(); 
    new Demo(); 
    new Demo(); 
    new Demo(); 
  } 
};
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
小问题: 
能不能不写主方法而打印出“HELLO WORLD”? 肯定可以,使用静态块哈~
public class Demo25 

  //在主类中写的静态块要优先于main方法执行 
  static
    System.out.println("Hello World!!!"); 
  } 
};
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
我们发现可以输出字符串“Hello World!!!”哈~~~但是程序还是要查找main方法哈,为了防止程序继续执行,我们这样修改代码哈~
public class Demo26 

  //在主类中写的静态块要优先于main方法执行 
  static
    System.out.println("Hello World!!!"); 
    //为了防止程序继续向后执行,去找main方法,此处可以使系统退出 
    System.exit(1); 
  } 
};
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
4、同步代码块
(在多线程中我们来讲哈~~~)
构造方法的私有
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
封装性:private 
可以声明一个属性,也可以声明一个方法,同样也可以使用private声明一个构造方法。
class Single 

        private Single(){} 
        public void print(){ 
                System.out.println("Hello World~~~"); 
        } 

public class Demo27 

        public static void main(String args[]) 
        { 
        //如果想调用print方法,则必须产生Single类的实例化对象 
        Single s = null
        s = new Single(); 
        } 
};
我们发现程序提示Single()被封装了哈~
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
class Single    
{    
        private Single(){}    
        public void getInstance()    
        {    
                //可以在类的内部产生自己的实例化对象    
                new Single();    
        }    
        public void print(){    
                System.out.println("Hello World~~~");    
        }    
}    
public class Demo27    
{    
        public static void main(String args[])    
        {    
        //如果想调用print方法,则必须产生Single类的实例化对象    
        Single s = null;    
        //s = new Single();    
        // 对象有引用传递。有没有一种可能可以在Single类的内部产生自己的对象呢?    
        }    
};
编译正常哈,可以在类的内部产生自己的实例化对象
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
class Single    
{    
        private Single(){}    
        public static Single getInstance()    
        {    
                //可以在类的内部产生自己的实例化对象    
                return new Single();    
        }    
        public void print(){    
                System.out.println("Hello World~~~");    
        }    
}    
public class Demo27    
{    
        public static void main(String args[])    
        {    
        //如果想调用print方法,则必须产生Single类的实例化对象    
        Single s = null;    
        s = Single.getInstance();    
        // 对象有引用传递。有没有一种可能可以在Single类的内部产生自己的对象呢?    
        s.print();    
        }    
}; 
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
结论: 
一个类的构造方法如果被私有化了,则外部肯定无法看见,则此时就可以在类的内部产生实例化对象之后将此对象传递到外面去就可以了。
构造方法私有化的目的:在于限制对象的产生 
· 如果一个类只能要求有一个实例化对象的时候呢? 
· 唯一可以做的就是在产生实例化对象的入口处加入限制 —— 构造方法的私有化

单态设计:
一个类永远只能产生一个实例化对象。  JAVA 类库中有很多地方都使用了单态。
//一个类只能产生一个实例Single@35ce36,这就叫单态设计    
class Single    
{    
        //在类内部产生自己的实例化对象,只实例化一次    
        private static Single s = new Single();    
        private Single(){}    
        public static Single getInstance()    
        {    
                //可以在类的内部产生自己的实例化对象    
                return s;    
        }    
        public void print(){    
                System.out.println("Hello World~~~");    
        }    
}    
public class Demo27    
{    
        public static void main(String args[])    
        {    
        //如果想调用print方法,则必须产生Single类的实例化对象    
        Single s1 = null;    
        s1 = Single.getInstance();    
        // 对象有引用传递。有没有一种可能可以在Single类的内部产生自己的对象呢?    
        s1.print();    
        //要产生第二个对象,也是取滴s对象    
        Single s2 = Single.getInstance();    
        System.out.println(s2);    
        System.out.println(s1);    
        }    
};
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)  
总结
本季重点哈~~~
[零基础学JAVA]Java SE面向对象部分-12.面向对象基础(07)
#############################################################################
ps:面向对象基础本季就完结了哈,下季学习面向对象高级部分:继承和多态O(^_^)O
#############################################################################







本文转自redking51CTO博客,原文链接:http://blog.51cto.com/redking/124733,如需转载请自行联系原作者