二、面向对象 Oriented Object
以类组织代码,以对象来封装数据
面向过程:以方法(函数)组织数据,以线性的思维去解决;当数据量少,无问题;当数据多了,方法多了,以
方法为核心组织代码,累人;扩展性差,不易维护(当多人协同项目时)
C语言结构体,多个变量集合在一块,方便管理;按照这个想法,横向竖向扩散/深入思考,把相近的方法也放在一起,方法反复调用变量,
深度发散,就把方法及变量打包到一块,这种概念在C语言里没有,就起名叫做类 class 【 YY】
以类来组织代码,易于维护,易于扩展
1000个变量 + 1000个方法 -----> 50个类
面向对象优势仅来自软件的复杂性,要是简单的话,就没有必要,用面向过程实现就可以
面向对象的思考方式: 以如何开汽车为例来阐述
面向过程:(事物比较简单,用线性思维去思考解决问题;跟把大象关进冰箱一样)
1.踩离合
2.挂档
3.踩油门,放离合
4.开了
面向对象:多角色,协作完成(思考这些过程要哪些对象)
对象:驾驶员、汽车
驾驶员开汽车
开车细节放在汽车下的方法, car.start(); 来实现
如何造汽车?
面向对象思维:轮胎、座椅、发动机、车架......,将上面造出的东西,组装起来,汽车造出
面向对象思维把握整体,面向过程实现局部细节
面向对象编程(OOP)的本质:以类的方式组织代码,以对象的方式组织(封装)数据
面向对象思维:OOA, OOD
对象:是具体的事物
类:是对对象的抽象,(抽象,抽出象的部分),类是对象的模板。 典型案例:数学 1-10 (1-10个人,1-10个苹果,1-10个等等)
现实的逻辑用代码来实现,静态的数据、动态的行为
===================================================================
JAVA语言中除基本类型之外的变量类型,都称之为引用类型;java对象是通过引用reference(地址)对其操作的
未赋值,默认初始化值
数值型:
--整型:0
--浮点型:0.0
--char型:\u0000
--boolean型:0 false
--字符串 null
属性(成员变量、静态数据);方法(动态行为),方法隶属于类
成员/实例变量、局部变量
局部变量必须初始化,成员/实例变量未赋值,系统采用默认值初始化
=======================程序执行过程的内存分析(初步分析浅,等待后期改进)====================
栈、堆、方法区
1.栈
自动分配连续的空间,后进先出
存放:局部变量
2.堆
空间不连续
存放:new出来的对象
3.方法区(在堆里划出一部分空间)
存放:类的信息(代码)、static变量、常量池(字符串常量)等
栈帧的概念 ? ? //后期补充
JVM在内存中未找到类,会从classpath里加载类,找到class文件,JVM通过类加载器class loader来加载类,加载后,在方法区就有student信息
操作对象,就是操作它的地址,引用类型4个字节
垃圾回收机制(Garbage Collection);空间分配,new对象;对象空间释放:GC回收没有被引用的对象
===============================构造器===================================
构造器又称构造方法constructor
格式如下:
[修饰符] 类名 (形参列表) {
//n条语句
}
构造方法是一种特殊的方法:
1.通过new来调用
2.定义时无需定义返回值;不能再构造器使用return语句
3.构造器的方法名必须和public class类名一致
4.如果没有定义构造器,编译器会在编译过程中自动构造一个无参构造器;如果已定义,则编辑器不会添加
5.初始化构造该类的对象
1 Car.java:2 public classCar {3 intspeed;4 publicCar(){5 System.out.println("构造一个车");6 }7 }8
9 TestConstructor.java10 public classTestConstructor {11 public static voidmain(String[] args) {12 Car c = newCar();13 }14 }15
16 编译执行结果:17 构造一个车18 构造类Car通过new来调用,同时也初始化对象c
============================方法重载 & 构造方法的重载===============================
重载(overload):最主要是调用时不会产生歧义(编译器不知道调用哪个?)
方法的重载是指一个类中可以定义有相同的名字,但参数不同的多个方法;调用时,会根据不同的参数表选择对应的方法
两同三不同
--两同:同一个类,同一个方法名
--三不同:参数列表不同(类型、个数、顺序不同)
只有返回值不同是不能构成方法的重载:int a(String str){} / void a(String i){},当调用a(String c),会产生歧义,不知道调用哪个?
只有形参名称不同,也不能构成方法的重载:int a(int b){} / int a(int c){},调用a(int d)会产生歧义
与普通方法一样,构造方法也可以重载;重载条件跟普通方法一致
1 public classTestOverload {2 public int add(int a, intb){3 return a+b;4 }5 public static voidmain(String[] args) {6 MyMath m = newMyMath();7 int result = m.add(4.2,8);8 System.out.println(result);9 }10 }11
12 classMyMath {13 inta;14 intb;15
16 publicMyMath(){17 }18
19 public MyMath(inta){20 this.a =a;21 }22
23 public MyMath(int b, inta){24 this.b =b;25 this.a =a;26 }27
28 public int add(int b, doublea){29 return (int)(a+b);30 }31
32 public int add(double a, intb){33 return (int)(a+b);34 }35
36 public int add(int a, intb){37 return a+b;38 }39
40 public int add(int a, int b,intc){41 return a+b+c;42 }43
44 }
====================================static静态===================================
在类中,用static声明的成员变量称为静态成员变量,或者叫做:类属性,类变量
--它为该类的公用变量,属于类,被该类的所有实例(对象)共享,在类被载入时被显式初始化
--对于该类的所有对象来说,static成员变量只有一份,被该类的所有对象共享
--可以使用“对象.类属性”来调用,不过,一般都是用“类名.类属性”
--static变量置于方法区中
用static声明的方法称为静态方法
--不需要对象,就可以调用(类名.方法名)
--在调用该方法时,不会将对象的引用传递给它,所以在static方法中不可访问非static的成员
静态初始化块:
如果希望加载后,对整个类进行初始化操作,可以使用static初始化块;是在类初始化时执行,不是在创建对象时执行
静态初始化块不能访问非static成员
执行顺序:上溯到Object类,先执行Object的静态初始化块,再向下执行子类的静态初始化块,直到我们的类的静态初始化块为止
静态的方法里不能调用非静态的内容
非静态的方法可以调用静态变量及静态方法
从属于类的方法,不能调用对象内容,对象里没有static
Student.javapublic classStudent {
String name;intid;static intss;public static voidprintSS(){
System.out.println(ss);
}public voidstudy(){
printSS();
System.out.println(name+"在學習");
}public voidsayHello(String sname){
System.out.println(name+"向"+sname+"說:你好!");
}
}
Test.javapublic classTest {public static voidmain(String[] args) {
Student.ss= 323; //类变量,一般都是用“类名.类属性”
Student.printSS();
Student s1= newStudent();
}
}
===========================================this=========================================
this用于方法里面,但不能用于static方法里;this隐式参数,jvm调用时会传this参数,方法里可以使用this
普通方法:this总是指向调用该方法的对象
构造方法:this总是指向正要初始化的对象
隐式参数:this super
this();调用其它构造器方法,必须放在第一句
Student.javapublic classStudent {
String name;intid;public Student(String name,intid){this(name); //通过this调用其他构造方法,必须位于第一句! Constructor call must be the first statement in a constructor
this.name =name;this.id =id;
}publicStudent(String name){this.name =name;
}publicStudent(){
System.out.println("构造一个对象");
}public voidsetName(String name){this.name =name;
}public voidstudy(){
System.out.println(this.name);this.name= "张三";
System.out.println(name+"在學習");
}public voidsayHello(String sname){
System.out.println(name+"向"+sname+"說:你好!");
}
}
Test.javapublic classTest {public static voidmain(String[] args) {//TODO Auto-generated method stub
Student s1 = newStudent();
s1.name= "李四";
s1.study();
}
}
执行结果:
构造一个对象
李四
张三在學習
构造方法:this总是指向正要初始化的对象
普通方法:this总是指向调用该方法的对象
this的引用地址new对象的引用地址
面向对象三大特征:
===================================继承======================================
OOD角度上看,类是对对象的抽象,继承是某一批类的抽象,从而实现对现实世界更好的建模 //inherited
OOP角度上看,提高代码的复用性
extends “扩展” 子类是对父类的扩展 (class a extends b)
子类继承父类,可以使用父类的全部属性和方法(除父类的构造器外)
java类只有单继承,利于维护(例如:只有一个直属上级,无多个)
java中的多继承,可以通过接口interface实现
如果定义一个类时,没有调用extends,则它的父类是java.lang.Object(根类)
方法的重写: 当从父类继承下来的方法不合适,就需要进行方法的重写;方法的重写跟方法的重载无任何关系
(阅读源代码,需要转换下思维;中文:亚洲、中国、北京、大兴区、街道 | 英文:街道、大兴区、北京、中国、亚洲;
鼠标停留到要查看的类,按住CTRL,跳到类文件里,用来查看源代码;Ctrl + T,查看源码层次)
重写的方法必须跟被重写具有相同的方法名称,参数列表和返回类型
隐式参数: this,super public void run (this,super) {};
this:当前对象的引用(地址)
super:指向父类对象的引用(地址),通过super访问父类的属性或值
构造方法:所有的构造方法,第一句super,调用父类的构造器,除了Object类;如果不加,编译器自动添加
1 Bird
3 Animal.java4 public classAnimal {5 String eye;6
7 public voidrun(){8 System.out.println("跑跑!");9 }10 public voideat(){11 System.out.println("吃吃!");12 }13 public voidsleep(){14 System.out.println("zzzzz");15 }16
17 publicAnimal(){18 super(); //通过super可以调用父类的构造器
19 System.out.println("创建一个动物!");20 }21
22 }23
24 class Bird extends Animal { //extends关键字 继承
25 public voidrun(){26 //this.run();
27 super.run();28 System.out.println("我是一个小小小小鸟,飞呀飞不高");29 }30
31 public voideggSheng(){32 System.out.println("卵生");33 }34
35 publicBird(){36 super();37 System.out.println("新建一个鸟对象");38 }39
40 }41
42 Test.java43 public classTest {44
45 public static voidmain(String[] args) {46 //TODO Auto-generated method stub
47 Bird b = newBird();48 b.run();49 }50 }51
52 执行结果:53 新建一个鸟对象 //构造器初始化
54 跑跑! //super调用父类
55 我是一个小小小小鸟,飞呀飞不高56
继承 VS 组合
从代码复用角度来看,组合完全可以替代继承,组合比继承更灵活;组合,把父类放在子类里,这样子类就可以使用父类的方法和属性
从建模角度来看,继承是继承,组合是组合;
组合的样例:
1 public classAnimal2 {2 String eye;3
4 public voidrun(){5 System.out.println("跑跑!");6 }7 public voideat(){8 System.out.println("吃吃!");9 }10 public voidsleep(){11 System.out.println("zzzzz");12 }13
14 publicAnimal2(){15 super();16 System.out.println("创建一个动物!");17 }18
19 public static voidmain(String[] args) {20 Bird2 b = newBird2();21 b.run();22 b.animal2.eat(); //子类调用父类的方法
23 }24
25 }26
27 classBird2 {28 Animal2 animal2=new Animal2();//组合,把父类放在子类里,这样子类可以使用父类的方法及变量
29
30 public voidrun(){31 animal2.run();32 System.out.println("我是一个小小小小鸟,飞呀飞不高");33 }34
35 public voideggSheng(){36 System.out.println("卵生");37 }38
39 publicBird2(){40 //super();
41 System.out.println("建一个鸟对象");42 }43 }
final 关键字
--修饰变量:常量
--修饰方法:该方法不可被子类重写,但可以重载
--修饰类:不能有子类,不能被继承;比如Math类,String类
final不能被修改
1 public /*final*/ class Animal { //final修饰类则说明,这个类不能被继承!
2
3 public /*final*/ void run(){ //final加到方法前面,意味着该方法不能被子类重写!
4 System.out.println("跑跑!");5 }6
7 }8
9 classBird extends Animal {10
11 public voidrun(){12 super.run();13 System.out.println("我是一个小小小小鸟");14 }15
16 }17 测试final18 public classTestFinal {19 public static voidmain(String[] args) {20 final int MAX_VALUE= 200; //常量
21 double d =Math.PI;22 }23 }
===================================封装/隐藏encapsulation=======================================
为什么需要封装?封装的作用和含义?
比如:电视只需要了解怎么使用,不需要了解内部信息
隐藏对象内部的复杂性,只对外公开简单的接口,便于外界调用,从而提高系统可扩展性、可维护性
哪些方法、类、属性封装?哪些开放?怎么实现?
private
default
protected
public
常用是private和public
成员变量全部私有,静态变量、常量可以公开
提供相应get/set方法来访问相关属性,这些方法通常是public,从而提供接口
1 public classMan {2 privateString name;3 privateString id;4 privateboolean man;5 public static intcc;6 public static final int MAX_SPEED = 120;7
8 publicString getName(){9 returnname;10 }11 public voidsetName(String name){12 this.name =name;13 }14 publicString getId() {15 returnid;16 }17 public voidsetId(String id) {18 this.id =id;19 }20 publicboolean isMan() {21 returnman;22 }23 public voidsetMan(boolean man) {24 this.man =man;25 }26
27 }
================================多态Polymorphism=======================
多态是为了适应需求的多种变化,使代码变得更加通用
多态的存在3个必要条件:
继承、方法的重写(方法的多态)、父类引用指向子类对象 eg:Animal a = new Cat();
多态性是OOP中的一个重要特性,主要是用来实现动态联编的,换句话来讲,就是程序的最终状态只有在执行过程中才被决定而非在编译期间,这对于大型系统来说能提高系统的灵活性和扩展性
如何实现多态?使用多态的好处?
引用变量的两种类型:
--编译时类型(父类定义宽广些,模糊点),由声明的类型决定
--运行时类型(运行时,具体是哪个子类就是哪个子类),由实际对应的对象类型决定
在调用时,有时需要强制转型: Cat a2 = (Cat)a; 或instanceof用于强制转换
1 Animal.java2 public classAnimal {3 String str;4 public voidvoice(){5 System.out.println("普通动物叫声!");6 }7 }8
9 classCat extends Animal {10 public voidvoice(){11 System.out.println("喵喵喵");12 }13 public voidcatchMouse(){14 System.out.println("抓老鼠");15 }16 }17
18 classDog extends Animal {19 public voidvoice(){20 System.out.println("汪汪汪");21 }22
23 public voidseeDoor(){24 System.out.println("看门!");25 }26
27 }28
29 Test.java30 public classTest {31
32 public static voidtestAnimalVoice(Animal c){33 c.voice();34 if(c instanceof Cat){35 ((Cat) c).catchMouse();36 }37 }38
39 public static voidmain(String[] args) {40 Animal a = newCat();41 Cat a2 = (Cat)a; //强制转换,否则无法调用catchMouse方法
42 a2.catchMouse();43 testAnimalVoice(a);44
45 }46 }
Servlet:服务器端小程序,本质是一个类
1 HttpServlet.java2
3 public classHttpServlet {4 public voidservice(){5 System.out.println("HttpServlet.service()");6 this.doGet();7 }8
9 public voiddoGet(){10 System.out.println("HttpServlet.doGet()");11 }12 }13
14 MyServlet.java15
16 public classMyServlet extends HttpServlet {17
18 public voiddoGet(){19 System.out.println("MyServlet.doGet()");20 }21
22 }23
24 A.javapublic classA {25 public static voidmain(String[] args) {26 HttpServlet s = newMyServlet();27 s.doGet();28 s.service();29 }30
31 }32 运行结果:33 MyServlet.doGet()34 HttpServlet.service()35 MyServlet.doGet()
=========================================抽象Abstract=======================================
抽象类,为什么需要抽象类?如何定义抽象类?
--是一种模板模式;抽象类为所有子类提供了一个通用模板,子类可以在这个模板基础上进行扩展
--通过抽象类,可以避免子类设计的随意性;通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用
要点:
1.有抽象方法的类,只能定义为抽象类
2.抽象类不能实例化,及不能用new来实例化抽象类;未抽象的子类可以new实例化,子类指向父类,引用父类
3.抽象类可以包含属性、方法、构造方法
4.抽象类无对象建立,只能被继承;一个抽象类可以继承另一个抽象类
5.抽象方法必须被子类实现 (父类定义规范,子类必须实现;比如父类定义run(),子类必须重写方法run)
抽象方法只有方法声明,无方法体
public abstract void run(); //抽象方法,只有声明,没有实现,具体实现由子类完成
子类必须定义run方法
@Override //重写
public void run(){
System.out.println("gogo");
}
1 Animal.java2 public abstract classAnimal {3 String str;4 public voidbreath(){5 System.out.println("breath");6 }7 public abstract void run(); //只有声明,无方法体
8 publicAnimal(){9 System.out.println("chaungjainyigezhongduw");10 }11 }12
13 /*abstract*/
14 classCat extends Animal {15 //不定义,由父类提供,不规范
16 @Override17 public voidrun() {18 System.out.println("miao");19 }20 }21 classDog extends Animal {22 //不定义,由父类提供,不规范
23 @Override24 public voidrun() {25 System.out.println("汪汪");26 }27 }28
29 Test.java30 public classTest {31 public static voidmain(String[] args){32 Animal a = newCat();33 a.run();34 }35 }
=================================Interface==============================
接口Interface:只有声明,没有实现,比抽象类还要抽象的类,
一个类可以实现多个接口
定义接口,格式:
[访问修饰符] interface 接口名 [extends 父接口1,父接口2..]{
常量定义 // public static final
方法定义 // public abstract
}
子类通过implements实现接口中的规范
interface无法new实例化,可以用于声明引用变量类型
一个类实现接口,必须实现接口中的所有方法,并且这些方法只能是public的;接口支持多继承
为什么需要接口?接口和抽象类的区别?
--接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全面专业地实现了:规范和具体实现的分离
--接口就是规范,定义的是一组规则,体现了现实世界中“如果你是...则必须能..."的思想。eg:如果你是天使,必须会飞
--接口的本质是契约,就像我们人间的法律一样,制定好后大家都遵守
--项目的具体需求是多变的;开发项目往往都是面向接口的编程
1 MyInterface.java2 public interfaceMyInterface {3 //接口中只有:常量、抽象方法!4 //public static final String MAX_GREAD = "BOSS";
5 String MAX_GREAD = "BOSS";6 int MAX_SPEED = 120;7
8 public voidtest01();9 public int test02(int a,intb);10
11 }12
13 interfaceInterfaceC extends MyInterface {14 //接口可以继承
15 }16
17 MyClass.java18 public classMyClass implements MyInterface {19 //接口里的方法必须被重写
20 @Override21 public voidtest01(){22 System.out.println("test01");23 }24
25 @Override26 public int test02(int a,intb){27 return (a+b);28 }29 }30
31 Test.java32 public classTest {33 public static voidmain(String[] args){34 //接口无法new
35 MyInterface a = newMyClass();36 a.test01();37 }38 }