二、面向对象 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 class Car { 3 int speed; 4 public Car(){ 5 System.out.println("构造一个车"); 6 } 7 } 8 9 TestConstructor.java 10 public class TestConstructor { 11 public static void main(String[] args) { 12 Car c = new Car(); 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 class TestOverload { 2 public int add(int a, int b){ 3 return a+b; 4 } 5 public static void main(String[] args) { 6 MyMath m = new MyMath(); 7 int result = m.add(4.2,8); 8 System.out.println(result); 9 } 10 } 11 12 class MyMath { 13 int a; 14 int b; 15 16 public MyMath(){ 17 } 18 19 public MyMath(int a){ 20 this.a = a; 21 } 22 23 public MyMath(int b, int a){ 24 this.b = b; 25 this.a = a; 26 } 27 28 public int add(int b, double a){ 29 return (int)(a+b); 30 } 31 32 public int add(double a, int b){ 33 return (int)(a+b); 34 } 35 36 public int add(int a, int b){ 37 return a+b; 38 } 39 40 public int add(int a, int b,int c){ 41 return a+b+c; 42 } 43 44 }
====================================static静态===================================
在类中,用static声明的成员变量称为静态成员变量,或者叫做:类属性,类变量
--它为该类的公用变量,属于类,被该类的所有实例(对象)共享,在类被载入时被显式初始化
--对于该类的所有对象来说,static成员变量只有一份,被该类的所有对象共享
--可以使用“对象.类属性”来调用,不过,一般都是用“类名.类属性”
--static变量置于方法区中
用static声明的方法称为静态方法
--不需要对象,就可以调用(类名.方法名)
--在调用该方法时,不会将对象的引用传递给它,所以在static方法中不可访问非static的成员
静态初始化块:
如果希望加载后,对整个类进行初始化操作,可以使用static初始化块;是在类初始化时执行,不是在创建对象时执行
静态初始化块不能访问非static成员
执行顺序:上溯到Object类,先执行Object的静态初始化块,再向下执行子类的静态初始化块,直到我们的类的静态初始化块为止
静态的方法里不能调用非静态的内容
非静态的方法可以调用静态变量及静态方法
从属于类的方法,不能调用对象内容,对象里没有static
Student.java public class Student { String name; int id; static int ss; public static void printSS(){ System.out.println(ss); } public void study(){ printSS(); System.out.println(name+"在學習"); } public void sayHello(String sname){ System.out.println(name+"向"+sname+"說:你好!"); } } Test.java public class Test { public static void main(String[] args) { Student.ss = 323; //类变量,一般都是用“类名.类属性” Student.printSS(); Student s1 = new Student(); } }
===========================================this=========================================
this用于方法里面,但不能用于static方法里;this隐式参数,jvm调用时会传this参数,方法里可以使用this
普通方法:this总是指向调用该方法的对象
构造方法:this总是指向正要初始化的对象
隐式参数:this super
this();调用其它构造器方法,必须放在第一句
Student.java public class Student { String name; int id; public Student(String name,int id){ this(name); //通过this调用其他构造方法,必须位于第一句! Constructor call must be the first statement in a constructor this.name = name; this.id = id; } public Student(String name){ this.name = name; } public Student(){ System.out.println("构造一个对象"); } public void setName(String name){ this.name = name; } public void study(){ System.out.println(this.name); this.name= "张三"; System.out.println(name+"在學習"); } public void sayHello(String sname){ System.out.println(name+"向"+sname+"說:你好!"); } } Test.java public class Test { public static void main(String[] args) { // TODO Auto-generated method stub Student s1 = new Student(); 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 <-- Animal <-- Object类 2 3 Animal.java 4 public class Animal { 5 String eye; 6 7 public void run(){ 8 System.out.println("跑跑!"); 9 } 10 public void eat(){ 11 System.out.println("吃吃!"); 12 } 13 public void sleep(){ 14 System.out.println("zzzzz"); 15 } 16 17 public Animal(){ 18 super(); //通过super可以调用父类的构造器 19 System.out.println("创建一个动物!"); 20 } 21 22 } 23 24 class Bird extends Animal { //extends关键字 继承 25 public void run(){ 26 //this.run(); 27 super.run(); 28 System.out.println("我是一个小小小小鸟,飞呀飞不高"); 29 } 30 31 public void eggSheng(){ 32 System.out.println("卵生"); 33 } 34 35 public Bird(){ 36 super(); 37 System.out.println("新建一个鸟对象"); 38 } 39 40 } 41 42 Test.java 43 public class Test { 44 45 public static void main(String[] args) { 46 // TODO Auto-generated method stub 47 Bird b = new Bird(); 48 b.run(); 49 } 50 } 51 52 执行结果: 53 新建一个鸟对象 //构造器初始化 54 跑跑! //super调用父类 55 我是一个小小小小鸟,飞呀飞不高 56
继承 VS 组合
从代码复用角度来看,组合完全可以替代继承,组合比继承更灵活;组合,把父类放在子类里,这样子类就可以使用父类的方法和属性
从建模角度来看,继承是继承,组合是组合;
组合的样例:
1 public class Animal2 { 2 String eye; 3 4 public void run(){ 5 System.out.println("跑跑!"); 6 } 7 public void eat(){ 8 System.out.println("吃吃!"); 9 } 10 public void sleep(){ 11 System.out.println("zzzzz"); 12 } 13 14 public Animal2(){ 15 super(); 16 System.out.println("创建一个动物!"); 17 } 18 19 public static void main(String[] args) { 20 Bird2 b = new Bird2(); 21 b.run(); 22 b.animal2.eat(); //子类调用父类的方法 23 } 24 25 } 26 27 class Bird2 { 28 Animal2 animal2=new Animal2();//组合,把父类放在子类里,这样子类可以使用父类的方法及变量 29 30 public void run(){ 31 animal2.run(); 32 System.out.println("我是一个小小小小鸟,飞呀飞不高"); 33 } 34 35 public void eggSheng(){ 36 System.out.println("卵生"); 37 } 38 39 public Bird2(){ 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 class Bird extends Animal { 10 11 public void run(){ 12 super.run(); 13 System.out.println("我是一个小小小小鸟"); 14 } 15 16 } 17 测试final 18 public class TestFinal { 19 public static void main(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 class Man { 2 private String name; 3 private String id; 4 private boolean man; 5 public static int cc; 6 public static final int MAX_SPEED = 120; 7 8 public String getName(){ 9 return name; 10 } 11 public void setName(String name){ 12 this.name = name; 13 } 14 public String getId() { 15 return id; 16 } 17 public void setId(String id) { 18 this.id = id; 19 } 20 public boolean isMan() { 21 return man; 22 } 23 public void setMan(boolean man) { 24 this.man = man; 25 } 26 27 }
================================多态Polymorphism=======================
多态是为了适应需求的多种变化,使代码变得更加通用
多态的存在3个必要条件:
继承、方法的重写(方法的多态)、父类引用指向子类对象 eg:Animal a = new Cat();
多态性是OOP中的一个重要特性,主要是用来实现动态联编的,换句话来讲,就是程序的最终状态只有在执行过程中才被决定而非在编译期间,这对于大型系统来说能提高系统的灵活性和扩展性
如何实现多态?使用多态的好处?
引用变量的两种类型:
--编译时类型(父类定义宽广些,模糊点),由声明的类型决定
--运行时类型(运行时,具体是哪个子类就是哪个子类),由实际对应的对象类型决定
在调用时,有时需要强制转型: Cat a2 = (Cat)a; 或instanceof用于强制转换
1 Animal.java 2 public class Animal { 3 String str; 4 public void voice(){ 5 System.out.println("普通动物叫声!"); 6 } 7 } 8 9 class Cat extends Animal { 10 public void voice(){ 11 System.out.println("喵喵喵"); 12 } 13 public void catchMouse(){ 14 System.out.println("抓老鼠"); 15 } 16 } 17 18 class Dog extends Animal { 19 public void voice(){ 20 System.out.println("汪汪汪"); 21 } 22 23 public void seeDoor(){ 24 System.out.println("看门!"); 25 } 26 27 } 28 29 Test.java 30 public class Test { 31 32 public static void testAnimalVoice(Animal c){ 33 c.voice(); 34 if(c instanceof Cat){ 35 ((Cat) c).catchMouse(); 36 } 37 } 38 39 public static void main(String[] args) { 40 Animal a = new Cat(); 41 Cat a2 = (Cat)a; //强制转换,否则无法调用catchMouse方法 42 a2.catchMouse(); 43 testAnimalVoice(a); 44 45 } 46 }
Servlet:服务器端小程序,本质是一个类
1 HttpServlet.java 2 3 public class HttpServlet { 4 public void service(){ 5 System.out.println("HttpServlet.service()"); 6 this.doGet(); 7 } 8 9 public void doGet(){ 10 System.out.println("HttpServlet.doGet()"); 11 } 12 } 13 14 MyServlet.java 15 16 public class MyServlet extends HttpServlet { 17 18 public void doGet(){ 19 System.out.println("MyServlet.doGet()"); 20 } 21 22 } 23 24 A.javapublic class A { 25 public static void main(String[] args) { 26 HttpServlet s = new MyServlet(); 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.java 2 public abstract class Animal { 3 String str; 4 public void breath(){ 5 System.out.println("breath"); 6 } 7 public abstract void run(); //只有声明,无方法体 8 public Animal(){ 9 System.out.println("chaungjainyigezhongduw"); 10 } 11 } 12 13 /*abstract*/ 14 class Cat extends Animal { 15 //不定义,由父类提供,不规范 16 @Override 17 public void run() { 18 System.out.println("miao"); 19 } 20 } 21 class Dog extends Animal { 22 //不定义,由父类提供,不规范 23 @Override 24 public void run() { 25 System.out.println("汪汪"); 26 } 27 } 28 29 Test.java 30 public class Test { 31 public static void main(String[] args){ 32 Animal a = new Cat(); 33 a.run(); 34 } 35 }
=================================Interface==============================
接口Interface:只有声明,没有实现,比抽象类还要抽象的类,
一个类可以实现多个接口
定义接口,格式:
[访问修饰符] interface 接口名 [extends 父接口1,父接口2..]{
常量定义 // public static final
方法定义 // public abstract
}
子类通过implements实现接口中的规范
interface无法new实例化,可以用于声明引用变量类型
一个类实现接口,必须实现接口中的所有方法,并且这些方法只能是public的;接口支持多继承
为什么需要接口?接口和抽象类的区别?
--接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全面专业地实现了:规范和具体实现的分离
--接口就是规范,定义的是一组规则,体现了现实世界中“如果你是...则必须能..."的思想。eg:如果你是天使,必须会飞
--接口的本质是契约,就像我们人间的法律一样,制定好后大家都遵守
--项目的具体需求是多变的;开发项目往往都是面向接口的编程
1 MyInterface.java 2 public interface MyInterface { 3 //接口中只有:常量、抽象方法! 4 //public static final String MAX_GREAD = "BOSS"; 5 String MAX_GREAD = "BOSS"; 6 int MAX_SPEED = 120; 7 8 public void test01(); 9 public int test02(int a,int b); 10 11 } 12 13 interface InterfaceC extends MyInterface { 14 //接口可以继承 15 } 16 17 MyClass.java 18 public class MyClass implements MyInterface { 19 //接口里的方法必须被重写 20 @Override 21 public void test01(){ 22 System.out.println("test01"); 23 } 24 25 @Override 26 public int test02(int a,int b){ 27 return (a+b); 28 } 29 } 30 31 Test.java 32 public class Test { 33 public static void main(String[] args){ 34 //接口无法new 35 MyInterface a = new MyClass(); 36 a.test01(); 37 } 38 }