类的封装性(private)
封装属性:private 属性类型 属性名
封装方法:private 方法返回类型 方法名称(参数)
注意:
用private声明的属性或方法只能在其类的内部被调用,而不能在类的外部被调用。
匿名对象:
可以简单的理解为只使用一次的对象,即没有任何一个具体的对象名称引用它。
System.out.println(new Person().talk());
new Person()声明的对象并没有赋给任何一个Person类对象的引用,所以此对象只使用了一次,之后就会被Java的垃圾收集器回收。
构造方法:
构造方法所完成的主要工作是帮助新创建的对象赋初值
1、它具有与类名相同的名称
2、它没有返回值
对象的比较
有两种方式可用于对象间的比较,它们是“= =”运算符与equals()方法,
“= =”操作符用于比较两个对象的内存地址值是否相等,
equals()方法用于比较两个对象的内容是否一致。
问题:
下面两种String对象的声明方式到底有什么不同?
String str1 = new String("java");
String str2 = "java";
01 public class StringDemo
02 {
03 public static void main(String[] args)
04 {
05 String str1 = "java" ;
06 String str2 = new String("java") ;
07 String str3 = "java" ;
08 System.out.println("str1 == str2 ? --- > "+(str1==str2)) ;
09 System.out.println("str1 == str3 ? --- > "+(str1==str3)) ;
10 System.out.println("str3 == str2 ? --- > "+(str3==str2)) ;
11 }
12 }
输出结果:
str1 == str2 ? --- > false
str1 == str3 ? --- > true
str3 == str2 ? --- > false
由程序输出结果可以发现,str1与str3相等,这是为什么呢?上面提到过"=="是用来比较内存地址值得。现在str1与str3相等,则证明str1与str3是指向同一个内存空间的。可以用下图来说明:
由图中可以看出“java”这个字符串在内存中开辟的一个空间,而str1与str3又同时指向同一个内存空间,所以即使str1与str3虽然是分开声明的,但最终却指向了同一内存空间,而str2是用new关键字来开辟的空间,所以单独占有自己的一个内存空间,
另外,String对象的内容一旦声明就不能轻易改变,如果想改变一个String对象的值,则第一步要做的是先将原有的String引用断开,之后再开辟新的内存空间,而且如果用new关键字开辟String对象的内存空间的话,则实际上就开辟了两个内存空间,如下所示:
从上面(A)中不难发现,String对象一旦声明则不能轻易改变,如果要改变则需要先断开原有的对象引用,再开辟新的对象,之后再指向新的对象空间。
(B)的方法也可以实现改变String对象的声明的操作,可以发现,用new String("java")方式实例化String对象时,实际上是开辟了两个内存空间,所以一般在开发商都采用直接赋值的方式,即: String str1 = "java" 。
static静态变量:
范例:TestStaticDemo1.java
01 class Person
02 {
03 String name ;
04 String city ;
05 int age ;
06 public Person(String name, String city, int age)
07 {
08 this.name = name ;
09 this.city = city ;
10 this.age = age ;
11 }
12 public String talk()
13 {
14 return "我是:"+this.name+",今年:"+this.age+"岁,来自:"+this.city;
15 }
16 }
17 public class TestStaticDemo1
18 {
19 public static void main(String[] args)
20 {
21 Person p1 = new Person("张三",25,"中国") ;
22 Person p2 = new Person("李四",30,"中国") ;
23 Person p3 = new Person("王五",35,"中国") ;
24 System.out.println(p1.talk()) ;
25 System.out.println(p2.talk()) ;
26 System.out.println(p3.talk()) ;
27 }
28 }
由上面的程序可以发现,所有的Person对象都有一个city属性,而且所有的属性也全部相同,如下所示:
读者可以试着想一想,现在假设程序产生了50个Person对象,如果想修改所有人的city属性的话,是不是就要调用50遍city属性,进行重新修改,显然太麻烦了。所以在java中提供了static关键字,用它来修饰类的属性后,则此属性就是公共属性了。将TestStaticDemo1.java程序稍作修改就形成了范例TestStaticDemo2.java
范例:TestStaticDemo2.java
01 class Person
02 {
03 String name ;
04 static String city = "中国";
05 int age ;
06 public Person(String name,int age)
07 {
08 this.name = name ;
09 this.age = age ;
10 }
11 public String talk()
12 {
13 return "我是:"+this.name+",今年:"+this.age+"岁,来自:"+city;
14 }
15 }
16 public class TestStaticDemo2
17 {
18 public static void main(String[] args)
19 {
20 Person p1 = new Person("张三",25) ;
21 Person p2 = new Person("李四",30) ;
22 Person p3 = new Person("王五",35) ;
23 System.out.println("修改之前信息:"+p1.talk()) ;
24 System.out.println("修改之前信息:"+p2.talk()) ;
25 System.out.println("修改之前信息:"+p3.talk()) ;
26 System.out.println(" ************* 修改之后信息 **************");
27 // 修改后的信息
28 p1.city = "美国" ;
29 System.out.println("修改之后信息:"+p1.talk()) ;
30 System.out.println("修改之后信息:"+p2.talk()) ;
31 System.out.println("修改之后信息:"+p3.talk()) ;
32 }
33 }
程序只在第28行修改了city属性,而且只修改了一个对象的city属性,但再次输出时,发现全部的对象的city值都发生了一样的变化,这就说明了用static声明的属性是所有对象共享的。
所有的对象都指向同一个city属性,只要当中有一个对象修改了city属性的内容,则所有对象都会被同时修改
另外,读者也需要注意一点,用static方式声明的属性,也可以用类名直接访问,拿上面的程序来说,如果想修改city属性中的内容,可以用如下方式:
Person.city = “美国” ;
注意:
既然static类型的变量是所有对象共享的内存空间,也就是说无论最终有多少个对象产生,也都只有一个static类型的属性,那可不可以用它来计算类到底产生了多少个实例对象呢?读者可以想一想,只要一个类产生一个新的实例对象,都会去调用构造方法,所以可以在构造方法中加入一些记数操作,如下面的程序:
class Person
{
static int count = 0 ; // 声明一static类型的整型变量
public Person()
{
count++ ; // 增加了一个对象
System.out.println("产生了:"+count+"个对象!");
}
}
public class TestStaticDemo3
{
public static void main(String[] args)
{
new Person() ;
new Person() ;
}
}
理解main方法:
如果一个类要被Java解释器直接装载运行,这个类中必须有main()方法。
由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数。
向java中传递参数可用如下的命令:
java 类名称参数1 参数2 参数3
public class TestMain
{
/*
public:表示公共方法
static:表示此方法为一静态方法,可以由类名直接调用
void:表示此方法无返回值
main:系统定义的方法名称
String args[]:接收运行时参数
*/
public static void main(String[] args)
{
// 取得输入参数的长度
int j = args.length ;
if(j!=2)
{
System.out.println("输入参数个数有错误!") ;
// 退出程序
System.exit(1) ;
}
for (int i=0;i<args.length ;i++ )
{
System.out.println(args[i]) ;
}
}
}
静态代码块
一个类可以使用不包含在任何方法体中的静态代码块,当类被载入时,静态代码块被执行,且只执行一次,静态代码块经常用来进行类属性的初始化。如下面的程序代码所示:
class Person
{
public Person()
{
System.out.println("1.public Person()");
}
// 此段代码会首先被执行
static
{
System.out.println("2.Person类的静态代码块被调用!");
}
}
public class TestStaticDemo5 {
// 运行本程序时,静态代码块会被自动执行
static
{
System.out.println("3.TestStaticDemo5类的静态代码块被调用!");
}
public static void main(String[] args) {
System.out.println("4.程序开始执行!");
// 产生两个实例化对象
new Person() ;
new Person() ;
}
}
从程序运行结果中可以发现,放在TestStaticDemo5类中的静态代码块首先被调用,这是因为程序首先执行TestStaticDemo5类,所以此程序的静态代码块会首先被执行。程序在22、23行产生了两个匿名对象,可以发现Person类中的静态代码块只执行了一次,而且静态代码块优先于静态方法,由此可以发现,静态代码块可以为静态属性初始化。对象数组的使用