第6章 面向对象基础目录 6.1 面向对象编程思想 6.2 面向对象五大原则 6.2.1 单一职责原则(SRP) 6.2.2 开放封闭原则(OCP) 6.2.3 里氏替换原则(LSP) 6.2.4 依赖倒置原则(DIP) 6.2.5 接口隔离原则(ISP) 6.3 类的定义 6.4 对象的创建与使用 6.5 垃圾回收机制 6.6 构造方法 6.7 代码封装修饰符 6.8 练习
面向对象编程包括: 面向对象的分析(OOA,Object Oriented Analysis) 面向对象的设计(OOD,Object Oriented Design) 面向对象的编程实现(OOP,Object Oriented Programming)
面向对象思想说明: OOA:通常是把构成问题域的事务分解成各个对象,给对象赋予相应的属性和行为,通过多个对象的相互协调来解决问题。 OOD:这些对象没有先后顺序,共同作用才构成了整个系统。我们只要用代码设计出这几个类型的对象,然后让他们互相通信、传递消息就可以完成系统功能。 OOP:编写代码。包括定义类名->编写类的属性->编写类的方法。
面向对象的思想: 分析系统中涉及的事务,根据“对象”抽象出“类”;找出对象共有的特点,并且在类中定义为属性;找出对象共有的行为,并且定义为类的方法。 类与对象的关系:对象抽象出类,类实例化为对象。
6.2.1单一职责原则(SRP)
一个类应该有且只有一个去改变它的理由,这就意味着一个类应该只有一项工作。
6.2.2开放封闭原则(OCP)
对象或实体应该对外扩展开放,对修改封闭。
6.2.3里氏替换原则(LSP)
在对象x为类型T时q(x)成立,那么当S是T的子类时,对象y为类型S时q(y)也成立。即对父类的调用同样适用于子类。
6.2.4依赖倒置原则(DIP)
高层次的模块不应该依赖低层次的模块,它们都应该依赖于抽象。具体实现依赖于抽象,而不是抽象依赖于具体实现。
6.2.5接口隔离原则(ISP) 不应强迫客户端实现一个它用不上的接口,或是说客户端不应该被迫依赖它们不使用的方法,使用多个专门的接口比使用单个接口要好得多。
类就是把相似的对象归为一个类。在软件设计中,类就是一个模板,它定义了一个特定种类的所有对象的属性(变量)和行为(方法)。 类的格式为:
[修饰符] class 类名{ 类的成员 …… }其中类的成员有:成员变量(属性)、成员方法(方法)、嵌套类和代码块。 类名单词首字母大写,属性和方法的第一个单词小写,其他单词首字母大写。 例: public class CPU {
}
属性和局部变量的对比: 在方法体中声明的变量、方法中的参数或代码块中声明的变量,都是局部变量。 例1:
public Computer(){ //初始化的概念:其实就是第一次给变量进行赋值 machine = new MainMachine(); keyBoard = new KeyBoard(); display = new Display(); mouse = new Mouse(); }上例中,machine,keyBoard,display,mouse都是局部变量。 属性可以和局部变量同名时,会产生名称冲突。可以使用this关键字表示类的属性。 例2: 属性:
private MainMachine machine; private Display display; private KeyBoard keyBoard; private Mouse mouse;局部变量:
public Computer(MainMachine machine, Display display, KeyBoard keyBoard,Mouse mouse) { super(); //当属性与局部变量名名称相同时,会产生名称冲突 //可以使用this关键词表示类的属性 this.machine = machine; this.display = display; this.keyBoard = keyBoard; this.mouse = mouse; }属性在整个类中有效,无论在什么地方声明;局部变量必须先声明,然后才能使用。 例3:
package classroom.chap6; public class Shu { public void show() { System.out.println(age); } public int age; }对象创建语法:类名 对象名 = new 类名(); 例:NoteBook n = new NoteBook(); 在其他类中,使用对象名点运算符调用属相和方法。 引用类的属性:对象名.属性 引用类的方法:对象名.方法名(实参) 在类内的方法中,访问本类的属性和方法可以直接访问。 创建对象原理: Java程序是交由JVM执行的,根据《Java虚拟机规范》的规定,运行时数据区通常包括这几个部分:程序计数器(Program Counter Register)、Java栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(MethodArea)、堆(Heap)。 我们在实例化一个对象时,同时操作了栈内存和堆内存。 在栈内保存对象的首地址,即引用; 在堆内存中保存了对象的属性。 对对象的所有操作只能通过引用完成,一旦引用出栈释放没有任何引用指向该对象,对象就变成垃圾失效。 对象的创建: 1、编写一个类,编写的类不会占用内存。 例:
public class Employee{ public int age; }2、在主函数中创建对象。 例:
public static void main(String[] args){ Employee emp = new Employee(); }3、运行有主函数的类,只有主函数的类才能运行,无论有多少个类,只有主函数中创建对象,才会占用内存。 对象的创建与使用: 对象的属性分配在堆内存中,但是对象名分配在栈内存中。 对象名(变量名)也称为对象的引用(reference),引用在Java中是地址的同义词。除了8种基本数据类型的变量,其他变量都是引用类型变量。
匿名对象的创建: 创建完对象,在调用该对象的属性或方法时,也可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫匿名对象。 如果只使用该对象进行一次方法或属性调用,可以创建匿名对象。 例:
public class Employee{ public int age; public static void main(String[] args){ int a = new Employee().age; } }垃圾回收(Garbage Collection,GC),JDK7以后使用G1(GarbageFirst)机制。Java不需要显示回收内存,jvm会提供一个垃圾回收程序进行内存回收。 GC工作时会定期进行内存回收,也会根据内存的使用频率调整垃圾回收频率,或在代码中调用垃圾回收程序进行内存回收。可以通过调用System.gc()通知jvm进行垃圾回收。 垃圾回收机制作用于堆内存,与栈内存无关。
对象会被回收情况: 1.对象的引用被赋值为null: Person p = new Person( ); p = null; 2.使用的匿名对象: new Person( ).sayHello( ); 3.超出生命周期的。 垃圾回收程序是一般是在堆上分配空间不够的时候回自己进行一次GC,程序员不需要也不能主动释放内存。Java的内存释放由垃圾回收程序类进行释放。在Java中,内存的释放由垃圾回收程序进行管理,程序员不能直接进行释放。但程序员可以调用System.gc()运行垃圾回收器,但是不能指定时间。 例:
GameRole rGameRole = null;//新建一个栈 for(int i=0;i<1000;i++){ //在循环外,超过生命周期的对象都是垃圾 GameRole gameRole = new GameRole(); rGameRole = new GameRole();//指向不同的堆 } System.gc();//通知垃圾回收器回收垃圾;jvm收到通知后会调度GC进行内存回收 //内存泄露:当有一个堆内存一致有栈指针指向它,这块堆则无法进行回收。所有的对象都是通过构造器来创建的。创建对象:类名 对象名 = new 构造方法(); 构造方法注意事项: 识别合法的构造方法; 1.构造方法可以被重载,一个构造方法可以通过this关键字调用另一个构造方法,this语句必须位于构造方法的第一行; 重载:方法的重载(overload):重载构成的条件:方法的名称相同,但参数类型或参数个数不同,才能构成方法的重载。 2.当一个类中没有定义任何构造方法,Java将自动提供一个缺省构造方法; 3.子类通过super关键字调用父类的一个构造方法; 4.当子类的某个构造方法没有通过super关键字调用父类的构造方法,通过这个构造方法创建子类对象时,会自动先调用父类的缺省构造方法 5.构造方法不能被static、final、synchronized、abstract、native修饰,但可以被public、private、protected修饰; 6.构造方法不是类的成员方法; 7.构造方法不能被继承; 8.方法名与类相同,大小写也相同; 9.没有返回值(不需要写void); 10.当使用new关键字实例化对象时,需要调用构造方法; 11.构造方法一般用于实例化对象时申请内存使用;或实例化对象时初始化属性。Java编译器会默认java提供一个空参的构造方法(当没有编写空参构造方法时,会自动提供,如果编写了空参构造方法,则编译器不再提供构造方法。); 12.构造方法可以进行方法的重载。 例:
public Computer(){ //初始化的概念:其实就是第一次给变量进行赋值 machine = new MainMachine(); keyBoard = new KeyBoard(); display = new Display(); mouse = new Mouse(); }构造方法的调用: 1)、在其他类只能在创建对象时调用一次构造方法。 2)、在本类中调用,只能在本类的构造方法中调用其他重载的构造方法。要使用this关键字,并且代码只能在构造方法的第一行。根据实参确定调用的构造方法。 例:
public Computer(MainMachine machine, Display display, KeyBoard keyBoard, Mouse mouse) { super(); //当属性与局部变量名名称相同时,会产生名称冲突 //可以使用this关键词表示类的属性 this.machine = machine; this.display = display; this.keyBoard = keyBoard; this.mouse = mouse; }快键创建构造器的方法: 1)、有参构造器:右击->source->Generate Constructor using Fields->Select All->OK 例:
public GameRole(String name, String weapon, int attack, int endurace) { super(); this.name = name; this.weapon = weapon; this.attack = attack; this.endurace = endurace; }一个类显示编写的带参构造方法后,会屏蔽默认的空参构造方法。如果需要空参构造方法,必须显示编写一个。 2)、无参构造器:右击->source->Generate Constructor using Fields->Deselect All->OK 例:
public GameRole() { super(); }封装是指隐藏对象的属性和方法的实现细节,只公开对外接口。封装的好处有:隐藏实现的细节、方便修改的实现和只能通过规定方法访问。 封装关键字:
位置privatedefaultprotectedpublic同一个类是是是是同一个包中的类否是是是不同包的子类否否是是不同包且不是子类否否否是1.定义一个点类Point,包含2个成员变量x、y分别表示x和y坐标,2个构造器Point()和Point(int x0,y0),以及一个movePoint(intdx,int dy)方法实现点的位置移动,创建两个Point对象p1、p2,分别调用movePoint方法后,打印p1和p2的坐标。[必题]
package chap6; public class Point { private int x; private int y; //空参构造方法 public Point() { super(); } //有参构造方法 public Point(int x, int y) { super(); this.x = x; this.y = y; } public void movePoint(int x,int y) { this.x=x; this.y=y; System.out.println(x+":"+y); } //显示x和y的值 public String toString(){ return "Point [x="+x+",y="+y+"]"; } public static void main(String[] args) { Point p1 = new Point(); Point p2 = new Point(); p1.movePoint(5, 2); p2.movePoint(20, 32); System.out.println(p1); System.out.println(p2); } }2.定义一个矩形类Rectangle: [必做题]2.1 定义三个方法:getArea()求面积、getPer()求周长,showAll()分别在控制台输出长、宽、面积、周长。2.2 有2个属性:长length、宽width2.3 通过构造方法Rectangle(int width, int length),分别给两个属性赋值2.4 创建一个Rectangle对象,并输出相关信息
package chap6; import java.util.Scanner; public class Rectangle { private int length; private int width; //建立无参构造方法 public Rectangle() { super(); } //建立有参构造方法 public Rectangle(int length, int width) { super(); this.length = length; this.width = width; } public int getArea(){ int area=length*width; return area; } public int getPer(){ int per=2*(length+width); return per; } public void showAll(){ System.out.print(" 长: "+length+" 宽: "+width+" 面积: "+getArea()+" 周长: "+getPer()); } public static void main(String[] args) { System.out.print("请输入矩阵长和宽:"); Scanner cs = new Scanner(System.in); int length = cs.nextInt(); int width = cs.nextInt(); Rectangle rt = new Rectangle(length,width); rt.showAll(); cs.close(); } }3.定义两个类,描述如下: [必做题]4.1定义一个人类Person:4.1.1定义一个方法sayHello(),可以向对方发出问候语“hello,my name isXXX”4.1.2有三个属性:名字、身高、体重4.2定义一个PersonCreate类:4.2.1创建两个对象,分别是zhangsan,33岁,1.73;lishi,44,1.744.2.2分别调用对象的sayHello()方法。 Person类:
package chap6; public class Person { private String name; private double height; private double weight; public static void sayHello(){ System.out.println("hello,my name is XXX"); } public Person() { super(); } public Person(String name, double height, double weight) { super(); this.name = name; this.height = height; this.weight = weight; } //String类型默认显示类的地址 //public String toString(){}相当于重写方法,显示想要的输出结果 public String toString() { return "Person [name=" + name + ", height=" + height + ", weight=" + weight + "]"; } public static void main(String[] args) { // TODO Auto-generated method stub } }PersonCreate类:
package chap6; public class PersonCreate { public static void main(String[] args) { Person person=new Person("zhangshan",33,1.73); Person person2=new Person("lishi",44,1.74); System.out.println(person); System.out.println(person2); Person.sayHello(); } }