第五章,面向对象基础篇
5.3 封装性
• 为了解决属性必须封装且又必须访问的矛盾,只要是被封装的属性,必须通过setter和getter方法设置和取得。
• 为前面类的私有属性加上getter和setter方法
Public void setAge(int a){ Age = a; } Public int getAge(){ Return age; }
setter方法可以加上检测代码检测输入的数据是否合法。
5.4构造方法
Person per = null;声明对象时不调用构造方法。 Per = new Person();实例化对象时调用构造方法。
• 构造方法也是可以重载的。只要每个构造方法的参数类型或参数个数不同,即可实现重载。
5.5 匿名对象
匿名对象只在堆内存中开辟空间,而不在栈内存的引用。
new Person("cathy",30).tell(); //匿名对象
5.6 实例讲解
class Student{ private String stuno; public Student(){ public Student(String stuno){ this.setStuno(stuno); } } public float sum(){ return math+english; } public float avg(){ return this.sum/3; }
}
5.7 String
• ==是用来进行地址比较的
Str2 str3指向同一个空间,他们的地址值就是相同的。
内容比较用equals,
str2.equals(str3)
“hello”.equals("hello"),一个字符串可以调用string类中的方法,说明一个字符串就是一个string类的匿名对象。
• String str1 = “hello”;
使用这种方式定义字符串有一个好处,就是如果一个字符串已经被一个名称所引用,则以后再有相同的字符串声明时,就不会再重新开辟空间。
String str2 = “hello”; str2==str1.
java中有个字符串池。这种设计叫共享设计,设计思路是,对象池中保存多个对象,新实例化的对象如果已经在池中定义了,则不再重新定义,指向已存在的实例空间。
使用new关键字,无论如何都会重新开一个空间。
对于字符串,就直接赋值,不要采用new。
• 字符串的内容一旦声明不可改变。
非要改变就用stringbuffer类。
• String (char[] value)字符数组变字符串。这是一个构造方法 char[] toCharArray() 字符串变字符数组。这是一个普通方法 char charAt(int index)字符串中取出指定位置字符 int length()字符串长度 String trim() 清除左右两端空格 String[] split(String regex) 按指定字符串拆分
IO操作中经常会遇到字符串与byte数组或char数组之间的转换操作。
• length取得数组的长度。
length()取得字符串的长度。
5.8 引用传递及基本应用
1.引用传递
public class RefDemo02{ public static void main(String args[]){ String str1 = "hello" ; // 实例化字符串对象 System.out.println("fun()方法调用之前:" + str1) ; fun(str1) ; // 调用fun()方法 System.out.println("fun()方法调用之后:" + str1) ; } public static void fun(String str2){ // 此处的方法由主方法直接调用 str2 = "MLDN" ; // 修改字符串内容 } };
程序运行结果
fun()方法调用之前:hello
fun()方法调用之后:hello
因为字符串的内容一旦声明是不可改变的,改变的只是其内存地址的指向。
class Demo{ String temp = "hello" ; // 此处为了方便,属性暂时不封装 }; public class RefDemo03{ public static void main(String args[]){ Demo d1 = new Demo() ; // 实例化Demo对象,实例化之后里面的temp=30 d1.temp = "world" ; // 修改temp属性的内容 System.out.println("fun()方法调用之前:" + d1.temp) ; fun(d1) ; System.out.println("fun()方法调用之后:" + d1.temp) ; } public static void fun(Demo d2){ // 此处的方法由主方法直接调用 d2.temp = "MLDN"; // 修改temp值 } };
2.接收本类的引用
class Demo{ // 定义Demo类 private int temp = 30 ; // 声明temp属性并封装 public void fun(Demo d2){ // 接收本类的引用 d2.temp = 50 ; // 直接通过对象调用本类的私有属性 } public int getTemp(){ // getter return temp ; } public void setTemp(int t){ // setter temp = t ; } }; public class RefDemo04{ public static void main(String args[]){ Demo d1 = new Demo() ; // 实例化Demo对象 d1.setTemp(50) ; // 只能通过setter方法修改内容 d1.fun(d1) ; // 此处把Demo的对象传回到自己的类中 System.out.println("temp = " + d1.getTemp()) ; } };
3.person类中可以有一个书属性,这个属性可以是book类。book类中可以有主人属性,这个属性可以是person类
class Person{ // 定义Person类 private String name ; // 姓名 private int age ; // 年龄 private Book book ; // 一个人有一本书
private Person child; //一个人一个孩子,person类有一个属性,值是另一个person类。 public Person(String name,int age){ this.setName(name) ; this.setAge(age) ; } public void setName(String n){ name = n ; } public void setAge(int a){ age = a ; } public String getName(){ return name ; } public int getAge(){ return age ; } public void setBook(Book b){ book = b ; } public Book getBook(){ return book ; } }; class Book{ // 定义Book类 private String title ; // 标题 private float price ; // 价格 private Person person ; // 一本书属于一个人 public Book(String title,float price){ this.setTitle(title) ; this.setPrice(price) ; } public void setTitle(String t){ title = t ; } public void setPrice(float p){ price = p ; } public String getTitle(){ return title ; } public float getPrice(){ return price ; } public void setPerson(Person p){ person = p ; } public Person getPerson(){ return person ; } }; public class RefDemo05{ public static void main(String arg[]){ Person per = new Person("张三",30) ; Book bk = new Book("JAVA SE核心开发",90.0f) ; per.setBook(bk) ; // 设置两个对象间的关系,一个人有一本书 bk.setPerson(per) ; // 设置两个对象间的关系,一本书属于一个人
System.out.println("从人找到书 --> 姓名:" + per.getName()+";年龄:" + per.getAge() +";书名:" + per.getBook().getTitle() + ";价格:" + per.getBook().getPrice()) ; // 可以通过人找到书 System.out.println("从书找到人 --> 书名:" + bk.getTitle() + ";价格:" + bk.getPrice() + ";姓名:" + bk.getPerson().getName() + ";年龄:" + bk.getPerson().getAge()) ; // 也可以通过书找到其所有人 } };
5.9 this 关键字
1.可以使用this强调本类中的方法
2.表示类中的属性
public Person(String name){ name=name; } Vs. public Person(String name){ This.name=name; }
3.调用本类的构造方法
如果一个类中有多个构造方法,可以利用this关键字互相调用。
public Person(){ System.out.println("一个新的Person对象被实例化。") } public Person(String name, int age){ this(); this.name = name; }
使用this调用构造方法必须也只能放在构造方法的第一行。
this调用构造方法是一定要留一个构造方法作为出口,即程序中至少存在一个构造方法不使用this调用其他构造方法。
一般都将无参构造方法作为出口。
4.表示当前对象
class Person{ public Person(){ pass } public boolean compare(Person per){ Person p1 = this; Person p2 = per; //person对象中有一个方法compare,可以用传进来的per对象和当前对象作对比 } }
5.10 static 关键字
1.java中常用的内存区域
-
- 栈内存空间:保存所有的对象名称(更准确的说是保存了引用的堆内存空间的地址)
- 堆内存空间:保存了每个对象的具体属性内容
- 全局数据区:保存static类型的属性
- 全局代码区:保存所有的方法定义
2.类属性调用
类名称.static属性
Person.country = "b city"
3.使用static声明方法
public static String getCountry(){ return country; }
4.使用static属性统计一个类产生了多少个实例化对象
5.使用static为对象进行自动的编名操作
6.程序中的System.exit(1)表示系统退出,只要在exit()方法中设置一个非零的数字,则系统执行到此语句之后将退出系统。
7.如果一个方法要由主方法直接调用,则必须按以下格式声明:“punlic static 方法的返回值类型,方法名称(参数列表){}”。因为主方法是静态方法,而静态方法是不能调用非静态方法的,所以之前的方法声明处才必须加上static关键字。
5.11 代码块
1.普通代码块
2.构造块
构造块是直接写在类中的代码块,构造块优先于构造方法执行,每次实例化对象都会执行构造块中的代码。
class Demo{ { System.out.println("定义构造块"); } public Demo(){ System.out.println("构造方法"); } }
3.静态代码块
使用static声明的代码块。静态代码块优先于主方法执行,而在类中定义的静态代码块会优先于构造块执行,而且不管有多少个对象产生,静态代码块只执行一次。
class Demo{ static{ System.out.println("静态代码块"); } }
静态代码块的妙用:静态代码块优先于主方法执行,那么就可以直接使用静态代码块而不使用主方法向屏幕上打印hello world了。成功打印hello world后,会爆找不到主方法的异常。解决办法是,在程序输出完hello world之后直接让程序退出即可。
5.12 构造方法私有化
1.对构造方法进行封装
class Singleton{ private Singleton(){ }//此处将构造方法进行封装 } public class SingleDemo02{ public static void main(String args[]){ Singleton s1 = null; //可以声明对象 s1 = new Singleton();//错误,无法实例化 } }
解决方法
class Singleton{ //在内部产生本类的实例化对象,将属性封装 private static Singleton instance = new Singleton(); private Singleton(){ //将构造方法封装 } public static Singleton getInstance(){ //通过静态方法取得Singleton类的实例 return instance; } }
public class demo{ public static void main(String args[]){
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
}
}
instance,s1,s2 都指向同一个堆内存,不管有多少个对象,实际上只有一个实例。
在设计模式中将这样的设计成为单例设计模式,及无论程序怎样运行,Singleton类永远只会有一个实例化对象存在。
5.13 对象数组
1.对象数组的声明
类 对象数组名称[] = new 类[数组长度];
Person per[] = new Person[3]; per[0] = new Person("cathy"); per[1] = new Person(“tom”);
//分别为数组中的每个元素初始化,每一个都是对象,都需要单独实例化。
//声明一个对象数组,里面有三个对象,使用静态初始化方式
Person per[] = {new Person("张三"),new Person("李四"),new Person("王五")};
5.14 内部类
1.使用static定义内部类
用static定义的内部类变成了外部类,用static声明的内部类不能访问非static的外部类属性。(在实际应用中用的不多,但是不代表没有价值。http://bbs.csdn.net/topics/350021609)
内部类的第一个好处就体现出来了——隐藏你不想让别人知道的操作,也即封装性。
内部类的第二个好处——一个内部类对象可以访问创建它的外部类对象的内容,甚至包括私有变量!
2.在外部访问内部类
外部类.内部类 内部类对象 = 外部类.new 内部类();
Outer.Inner in = out.new Inner();
3.在方法中定义内部类
在方法中定义的内部类不能直接访问方法中的参数,如果方法中的参数想要被内部类所访问,参数钱必须加上final关键字。
class Outer{ // 定义外部类 private String info = "hello world" ; // 定义外部类的私有属性 public void fun(final int temp){ // 定义外部类的方法 class Inner{ // 在方法中定义的内部类 public void print(){ // 定义内部类的方法 System.out.println("类中的属性:" + info) ; // 直接访问外部类的私有属性 System.out.println("方法中的参数:" + temp) ; } }; new Inner().print() ; // 通过内部类的实例化对象调用方法 } }; public class InnerClassDemo05{ public static void main(String args[]){ new Outer().fun(30) ; // 调用外部类的方法 } };
实例
如果觉得分析类有困难,可以先把基本功能做完,做完之后对一些输入的数据进行验证,再把主方法中的代码尽可能减少,然后再去考虑代码的可重用性。
本章要点
如果一个对象没有被实例化而直接使用,则使用时会出现空指向异常。
类属于引用数据类型。进行引用传递时,传递的是堆内存的使用权。
使用构造方法实例化string时产生两个实例化对象,其中一个是垃圾空间。
如果需要限制类对象的产生可以将构造方法私有化。一旦将构造方法私有化,在类的外部就不能new新对象,只能在类的内部创建好一个对象然后调用这个对象。这样就限制了对象的产生。
对象数组的使用要分为声明数组和为数组开辟空间两步。开辟空间后数组中的每个元素的内容都是null。
在方法中声明的内部类要想访问方法的参数,则参数前必须加上final关键字。因为方法的变量属于局部变量,离开该方法,变量就失去了作用,也就会自动被消除,内部类却不会离开所在方法是就失去作用。