面向对象编程(OOP)
初识面向对象
面向对象&面向过程
-
面向过程思想:步骤简单清晰,第一步做什么,第二步做什么;面对过程适合处理一些较为简单的问题
-
面向对象思想:物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索;面向对象适合处理复杂的问题,适合处理需要多人协作的问题
-
对于描述复杂的事物,为了从宏观上把握,从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理
-
面向对象与面向过程不可分割。
什么是面向对象
-
面向对象编程(Object-Oriented Programming,OOP)
-
面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据
-
抽象
-
三大特性:封装;继承;多态
-
从认识角度考虑是现有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象
-
从代码运行角度考虑是现有类后有对象。类是对象的模板
方法回顾和加深
-
方法的定义:
-
修饰符
-
返回值类型
-
break和return的区别(break跳出循环,return结束方法返回一个结果)
-
方法名:驼峰原则,见名知意
-
参数列表:参数类型,参数名
-
异常抛出
-
列:
import java.io.IOException; //oopTest1 类 public class oopTest1 { // main 方法 public static void main(String[] args) { /* 修饰符 返回值类型 方法名(....){ 方法体 } return 返回值 */ } public String sayHello(){ return "hello,world"; } public int max(int a,int b){ //三元运算符 return a>b ? a : b; } //抛出异常 public void readFile(String file) throws IOException { } }
-
方法的调用
-
静态方法:加static关键字
-
非静态方法:没有static关键字
public class oopTest3 { public void say(){ //非静态方法,没有加static关键字只能在mian方法里 System.out.println("学生说话了"); } } public class oopTest2 { public static void main(String[] args) { //在oopTest3中say方法前加static关键词修饰才可以直接调用 //oopTest3.say(); //如果没有加static(非静态方法)需要将类实例化,new一个对象 oopTest3 oopTest3 = new oopTest3(); //调用格式:类名加方法名 oopTest3.say(); } //和类一起加载的 public static void a(){ //b();没有实列化不可调用 } //没有加static要类实列化之后才存在 public void b(){ } } 学生说话了 Process finished with exit code 0
-
形参和实参
public class oopTest3 { public static void main(String[] args) { //这里的3,和4才是实参,实际参数和形式参数类型要一一对应 int a=oopTest3.add(3,4); System.out.println(a); } //int a,int b中的a.b是形参 public static int add(int a,int b){ return a+b; } } 7 Process finished with exit code 0
-
值传递和引用传递
-
值传递
//值传递 public class oopTest4 { public static void main(String[] args) { int a=1; System.out.println(a); oopTest4.change(a); System.out.println(a); } //返回值为空没有将值传到上面 public static void change(int a){ a=10; } } 1 1 Process finished with exit code 0
-
引用传递
//引用传递:对象,本质还是值传递 public class oopTest5 { public static void main(String[] args) { //实列化的过程,这里调用的是下面的类中的name Person person = new Person(); System.out.println(person.name); oopTest5.change(person); System.out.println(person.name); } //将person 传递给Person public static void change(Person person){ /*这里修改的是String name中的name,person是一个对象 指向的是Person。Person person=new Person();这是一 个具体的人,可以改变属性 */ person.name="dpc"; } } //定义力一个person类,有一个属性:name class Person{ //默认值为空 String name; } null dpc Process finished with exit code 0
-
this关键字
类与对象的关系
-
类是一种抽象数据类型,他是对某一种类事物整体描述/定义,但是并不能代表某一个具体的事物
-
动物,植物,手机,电脑
-
Person类,Pet类,Car类等,这些类都是用来描述/定义某一类具体的事物因该具备的特点和行为
-
对象是抽象概念的具体实列
-
张三就是人的一个具体实列,张三家里的旺财就是狗的一个具体实列
-
能够体现出特点,展现出功能的是具体的实列,而不是一个抽象的概念
-
列:
-
创建一个学生类然后在Application中调用他的属性
//学生类 public class Student { //属性:字段,抽象的概念没有被写死 String name; int age; //方法 public void study(){ //this代表当前这个类,this.name代表当前类的name System.out.println(this.name+"在学习"); } }
-
在Application中进行实例化和调用
//一个项目应该只存在一个mian方法 public class Application { public static void main(String[] args) { /*类是抽象的需要实列化,通过new关键词 类实例化后会返回一个自己的对象 student对象就是Student类的具体实列 */ Student xiaoming = new Student(); Student xiaohong = new Student(); //这里不赋值的化打印的是null和0,应为调用的是Studeng中定义的默认值 xiaoming.name="小明"; xiaoming.age=16; System.out.println(xiaoming.name); System.out.println(xiaoming.age); xiaohong.name="小红"; xiaohong.age=17; System.out.println(xiaohong.name); System.out.println(xiaohong.age); } } 小明 16 小红 17 Process finished with exit code 0
对象的创建分析
创建与初始化对象
-
使用new关键字创建对象。
-
使用new关键字创建对象的时候,除了分配内存空间之外,还会给创建好的对象,进行默认的初始化以及对类中构造器的使用
-
类中的构造器也称为构造方法,实在进行创建对象的时候必须要调用的。并且构造器有以下两个特点:
-
必须和类的名字相同
-
必须没有返回类型,也不能写void
-
构造器必须要掌握
-
列:定义有参和无参构造器进行调用
//一个项目应该只存在一个mian方法 public class Application { public static void main(String[] args) { //("dpc");括号里传参数调用了有参构造 Person person=new Person("dpc"); System.out.println(person.name); } } //一个类即使什么也不写,它也会存在一个方法 public class Person { String name; //显示的定义构造器 //实例化初始值 //1,使用new关键字,本质是调用构造器 public Person(){ } //有参构造,一旦定义了有参构造,无参就必须显示定义 public Person(String name){ this.name=name; } //alt+insert快捷键自动生成构造器 } dpc Process finished with exit code 0
-
和类名相同;没有放回值
-
new本质在调用构造器
-
初始化对象的值
-
定义有参构造之后,如果想使用无参构造,显示的定义一个无参构造
-
ALT+Inser快捷键生成构造器
-
整理
-
类与对象:类是一个模板,抽象,对象是一个具体的实列
-
方法:定义,调用
-
对应的引用:引用类型,基本类型(8)对象是通过引用来操作的栈——>堆
-
属性:字段Filed 成员变量。默认初始化 数字 0,0.0 ; char u0000;boolean: false;引用 null
-
对象的创建和使用:必须使用new关键字创造对象,构造器
-
类: 静态的属性 属性;动态的行为 方法
面向对象三大特性
封装
-
该露的露,该藏的藏
-
我们程序设计要追求"高内聚,低耦合"。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用
-
封装(数据的隐藏)
-
通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称作信息隐藏
-
记住这句话就够了:属性私有,get/set
-
列:
-
Application
import com.oop.test1.Person; import com.oop.test2.Student; public class Application { public static void main(String[] args) { Student s1 =new Student(); //如果name属性用private修饰就不能调用 //s1.name = "dpc"; //利用setName传入值 s1.setName("dpc"); //用getName得到值 System.out.println(s1.getName()); //如果输入年龄过大是不合法的可以在set方法中进行限制 s1.setAge(70); System.out.println(s1.getAge()); } } dpc 70 Process finished with exit code 0
-
Student
package com.oop.test2; //属性私有 private:私有 public class Student { //名字(属性私有) private String name; //学号 private int id; //性别 private char sex; //年龄 private int age; //提供一些可以操作这个属性的方法 //提供一些public的get,set方法 //get 获得这个数据 public String getName(){ return name; } //set给这个数据设置值,设置那个数据就set哪一个值 public void setName(String name){ this.name = name; } //ALT+INSERT自动生成get set方法 //实际应用年龄不能过高 public int getAge() { return age; } public void setAge(int age) { if (age<=120&&age>0){ this.age = age; }else{ this.age=3; } } }
-
封装的好处:
提高程序的安全性,保护数据
隐藏代码的实现细节
统一接口
提高了系统的可维护性
继承
-
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
-
extends的意思是"拓展"。子类是父类的拓展
-
JAVA中的类只有单继承,没有多继承,一个子类只能有一个父类,一个父类可以有多个子类
-
继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖,组合,聚合等
-
继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示
-
子类和父类之间,从意义上讲应该具有"is a"的关系
-
objec,super,方法重写
-
列:
-
Application
import com.oop.extendsTest1.Student; public class Application { public static void main(String[] args) { Student student = new Student(); //Student里并没有只个方法从Person继承的 student.say(); student.setMoney(100000); System.out.println(student.getMoney()); } } 说了一句话 100000 Process finished with exit code 0
-
Person
//在Java中,所有的类都默认直接或间接继承object类 //person 人 public class Person /*extends object*/{ //公共的子类可以继承.private私有的不可以继承 //可用private对父类的一些数据进行封装 private int money; public void say(){ System.out.println("说了一句话"); } public int getMoney() { return money; } public void setMoney(int money) { this.money =money; } }
-
Student
package com.oop.extendsTest1; //学生 is 人:派生类 子类 //子类继承父类就会拥有父类的全部方法 public class Student extends Person{ //快捷键Ctrl+h打开继承树 }
-
super(父类的),this(当前的)
-
Application
import com.oop.extendsTest1.Student; public class Application { public static void main(String[] args) { Student student=new Student(); student.test("dpc2"); student.test1(); } } Perso无参执行了 Student无参执行了 dpc2 dpc1 dpc Student Student Person Process finished with exit code 0
-
Student
//学生 is 人:派生类 子类 //子类继承父类就会拥有父类的全部方法 public class Student extends Person{ private String name="dpc1"; public void test(String name){ //打印的是返回的参数 System.out.println(name); //打印的是当前定义的name System.out.println(this.name); //打印的是通过super关键字从父类访问的name System.out.println(super.name); } //在子类定义一个方法 @Override public void print(){ System.out.println("Student"); } //对父类的方法进行调用 public void test1(){ //调用当前类的方法 print(); this.print(); //在这个方法中调用了父类的方法 super.print(); } public Student(){ //隐藏代码:调用了父类的无参构造 super();//调用父类的构造器,必须要在子类构造器的第一行 System.out.println("Student无参执行了"); } }
-
super注意点:
super调用父类的构造方法,必须在构造方法的第一个
super必须只能出现在子类的方法或者构造方法中
super和this不能同时调用构造方法
-
this与super注意点:
代表的对象不同
this:本身调用者这个对象
super:代表父类对象的应用
前提
this:没有继承也可以使用
super:只能在继承条件才可以使用
构造方法
this();本类的构造
super();父类的构造
-
方法的重写:重点
-
Application
import com.oop.extendsTest1.Student; import com.oop.rewrite.A; import com.oop.rewrite.B; public class Application { public static void main(String[] args){ //方法的调用只和左边,定义的数据类型有关 //不重写的化调用A走A的方法调用B走B的方法 A a=new A(); a.test(); //父类的引用指向了子类,子类重写了父类的方法 B b=new A(); b.test(); //通过继承调用B中的方法 b.test1(); /*在A中对B的方法进行重写在打印会发现结果发生改变,两个都 走了A的方法,可以得出结论静态方法和非静态方法有很大区别, 应为静态方法是类的方法,而非静态方法是对象的方法,有static 时,b调用了B类的方法,应为b是用b类定义的,没有static时,b调 用的时对象的方法,而b是用A类new的机b是Anew出来的对象,因此调 用了A的方法 静态方法:方法的调用只和左边,定义的数据类型有关 非静态:重写(将public改为private不能重写),只能子类和父类才有 总结: 重写:需要有继承关系,子类重写父类的方法 1.方法名必须相同 2.参数列表必须相同 3.修饰符:范围可以扩大但不可以缩小:public>protected>default>private 4.抛出的异常:范围可以被缩小,但不能扩大 重写方法子类和父类必需一致,方法体不同; 为什么需要重写: 父类的功能,子类不一定需要,或不一定满足 Alt+Insert:override; */ } } A=>test A=>test B Process finished with exit code 0
-
A
public class A extends B{ @Override //注解:有功能的注释 //可以对父类的方法进行重写,左边出现向上箭头表示重写 public void test() { System.out.println("A=>test"); } }
-
B
//重写是方法的重写和属性无关 public class B { public void test() { System.out.println("B=>test"); } public void test1() { System.out.println("B"); } }
-
总结:
在A中对B的方法进行重写在打印会发现结果发生改变,两个都 走了A的方法,可以得出结论静态方法和非静态方法有很大区别, 应为静态方法是类的方法,而非静态方法是对象的方法,有static 时,b调用了B类的方法,应为b是用b类定义的,没有static时,b调 用的时对象的方法,而b是用A类new的机b是Anew出来的对象,因此调 用了A的方法 静态方法:方法的调用只和左边,定义的数据类型有关 非静态:重写(将public改为private不能重写),只能子类和父类才有 1.方法名必须相同 2.参数列表必须相同 3.修饰符:范围可以扩大但不可以缩小:public>protected>default>private 4.抛出的异常:范围可以被缩小,但不能扩大 重写方法子类和父类必需一致,方法体不同; 为什么需要重写:
父类的功能,子类不一定需要,或不一定满足
快捷键:Alt+Insert:override;
多态
-
动态编译:类型,可扩展性
-
即同一个方法可以更具发送对象的不同而采用不同的行为方式
-
一个对象的实际类型是确定的,但可以指向对象的引用类型很多
-
多态存在的条件
-
有继承关系
-
子类重写父类方法
-
父类引用指向子类对象
-
注意:多态是方法的多态,属性没有多态。instanceof
-
Application
import com.oop.polymorphism.Person; import com.oop.polymorphism.Student; public class Application { public static void main(String[] args) { //一个对象的实用类型是确定的,可以指向的引用类型就不确定了 //Student能调用的方法都是自己的或者父类的 Student s1 = new Student(); //父类的引用指向子类,不能调用子类独有的方法 Person s2 = new Student(); //子类重写了父类的方法,执行子类的方法 /*如果将s2.run改为s2.eat会报错,对象能执行那些方法 主要看对象左边的类型,和右边关系不大。如果让s1和s2都打印run 都会输出son,s1并不会输出run,如果不在Student中将run方法进 行重写这都会输出Person中的run,重载后输出Student中的son */ s2.run(); s1.run(); s1.eat(); //如果要用s2调用eat方法这里用了强制转换由高类型转为低类型 ((Student)s2).eat(); } } son son eat eat Process finished with exit code 0
-
Persion
public class Person { public void run(){ System.out.println("run"); } }
-
Student
public class Student extends Person{ @Override public void run(){ System.out.println("son"); } public void eat(){ System.out.println("eat"); } }
多态注意事项:
多态是方法的多态,属性没有多态
父类和子类,有联系才可以转换,没有会类型转换异常 ClassCastException!
存在的条件:继承关系,方法需要重写,父类的引用指向子类对象 father f1=new Son();
static 方法属于类,他不属于实列
final常量无法重写,private方法也无法重写
-
instanceof(类型转换)引用类型(判断一个对象是什么类型)
-
利用instanceof判断类之间的联系
com.oop.instanceofTest.Person; import com.oop.instanceofTest.Student; import com.oop.instanceofTest.Teacher; //System.out.println(x instanceof y);判断能不能编译通过 public class Application { public static <object> void main(String[] args) { //instanceof判断两个类型之间是否有联系 /* Object >String Object >Person >Teacher Object >Person >Student */ Object object =new Student(); System.out.println(object instanceof Student); System.out.println(object instanceof Person); System.out.println(object instanceof Object); System.out.println(object instanceof Teacher); System.out.println(object instanceof String); System.out.println("===================================="); Person person = new Student(); System.out.println(person instanceof Student); System.out.println(person instanceof Person); System.out.println(person instanceof Object); System.out.println(person instanceof Teacher); //Person和String是两条线没有关系就会报错 //System.out.println(person instanceof String);编译报错 System.out.println("===================================="); Student student = new Student(); System.out.println(student instanceof Student); System.out.println(student instanceof Person); System.out.println(student instanceof Object); //System.out.println(student instanceof Teacher);编译报错 //System.out.println(student instanceof String);编译报错 System.out.println("===================================="); } true true true false false ==================================== true true true false ==================================== true true true ==================================== Process finished with exit code 0
-
强制转换
import com.oop.instanceofTest.Person; import com.oop.instanceofTest.Student; public class Application { public static <object> void main(String[] args) { //类型之间的转化:父 子 //高 低 Person student = new Student(); //student将这个对象转换为Student类型,这样才可以使用Student中的方法 ((Student)student).go(); //将student转换为Person Person person=student; //子类转换成父类可能丢失自己的本来的一些方法 //student.go; } }
-
总结:
父类指向子类的对象
把子类转换为父类,向上转型;
把子类转换为父类,向下转型:强制转换
方便方法的调用,减少重复的代码、
static关键字详解
-
列:
-
Person
public class Person { { //第二个执行的,可以赋一些初始值 System.out.println("匿名代码块"); } static { //第一个执行的和类一起加载 System.out.println("静态代码块"); } public Person() { //第三个执行的 System.out.println("构造方法"); } public static void main(String[] args) { Person person1 = new Person(); System.out.println("====================="); //第二次输出静态代码块没有 Person person2 = new Person(); } } /* { //代码块(匿名代码块)创建对象时自动创建了,在构造器之前 } static{ //静态代码块,加载一些初始的东西,类一加载就直接执行,永久执行一次 } */ 静态代码块 匿名代码块 构造方法 ===================== 匿名代码块 构造方法 Process finished with exit code 0
-
Student
//static public class Student { //静态变量 private static int age; //非静态变量 private double score; //非静态方法 public void run(){ } //静态方法 public static void go(){ } public static void main(String[] args) { Student s1 = new Student(); System.out.println(Student.age); System.out.println(age); System.out.println(s1.score); //调用非静态方法需要new Stuent() new Student().run(); /*调用静态方直接调用。非静态方法可以调用静态方法里的东西, 静态方法可以调用静态方法的,而静态方法不可以调用非静态方法 的 */ go(); } } 0 0 0.0 Process finished with exit code 0
-
Teacher
//静态导入包 import static java.lang.Math.random; public class Teacher { public static void main(String[] args) { //调用Math中的randow方法,当上面使用静态导包后Math.可以省略 System.out.println(random()); } }
总结:静态代码块,加载一些初始的东西,类一加载就直接执行,永久执行一次,代码块(匿名代码块)创建对象时自动创建了,在构造器之前,调用静态方直接调用。非静态方法可以调用静态方法里的东西,静态方法可以调用静态方法的,而静态方法不可以调用非静态方法的。调用Math中的randow方法,当上面使用静态导包后Math.可以省略
抽象类和接口
抽象类
-
abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法,如果修饰类,那么该类就是抽象类
-
抽象类中可以没有抽象方法,但是游抽象方法的类一定要声明为抽象类
-
抽象类不能使用new关键字来创建对象,他是用来让子类继承的
-
抽象方法,只有方法的声明,没有方法的实现,他是用来让子类实现的
-
子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类
-
列:
-
Action
//abstract 变为抽象类 public abstract class Action { //约束 有人帮我们实现,就是让别人实现 //abstract,抽象方法,只有方法名字,没有方法的实现 public abstract void doSomething(); }
-
A
/*抽象类的所有方法都要由它的子类去实现,除非子类也是抽象 类那就由子子类实现。类时单继承的单接口可以多继承 */ public class A extends Action { @Override public void doSomething() { } }
-
特点:
不能new这个抽象类,只能靠子类去实现它:约束
一个类里面写了抽象方法那么这一类要声明为抽象类但这个类里可以写普通方法
抽象方法必须在抽象类中
提高开发效率
-
什么是构造器
构造器就是和类名相同但无返回类型的方法比如 class A { public A() { } } 其中A方法 就是class A的构造器 虽然构造器没有返回值但可以有参数如 class A { public A(String a,String b) { } } 一个类也可以有多个构造器如 class A { public A() { } public A(String a,String b) { } } 当你new A()时则无参那个构造器被调用,当你new A("ddd","fff")时,那个有参的构造器被调用,如果你没有写构造器则jvm会调用一个默认的无参的构造器 构造器在类初始化的时候被调用通常被用来做一些初始化的工作
接口
-
普通类:只有具体实现
-
抽象类:具体实现和规范(抽象方法)都有
-
接口:只有规范。声明类的关键字是class,声明接口的关键字是interface。自己无法写方法,专业的约束。约束和实现分离:面向接口编程
-
接口就是规范,定义的是一组规则,体现了现实世界中"如果你是...则必须能..."的思想。如果你是程序员你必须能写代码。接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
-
OO的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如c++,java,c#等),就是应为设计模式所研究的,实际上就是如何理解的去抽象
-
两个关键字:interface,定义一个抽象类,implemnts,继承接口的方法要记得重载
-
列:
-
总结:
接口时一种约束
可以定义一些方法,让不同的人实现
接口不能被实列化,接口中没有构造方法
可以通过implements去实现多个接口
必须要重写接口中的方法
内部类及OOP实战
内部类
-
内部类就是在一个类的内部在定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对于B类来说就是外部类了
-
成员内部类
-
Application
import com.oop.internalTest.Outer; public class Application { public static void main(String[] args) { Outer outer = new Outer(); //通过这个外部类来实列化内部类,当Inner没有加public则无法实列化 Outer.Inner inner=outer.new Inner(); inner.in(); inner.gerID(); } } 这是内部类的方法 10 Process finished with exit code 0
2.Outer
public class Outer { private int id=10; public void out(){ System.out.println("这是外部类的方法"); } public class Inner{ public void in(){ System.out.println("这是内部类的方法"); } //内部类可以访问外部类的一些私有属性,私有方法 public void gerID(){ System.out.println(id); } } }
-
静态内部类
-
Outer
public class Outer { private int id=10; public void out(){ System.out.println("这是外部类的方法"); } //静态内部类无法访问外部类 public static class Inner{ public void in(){ System.out.println("这是内部类的方法"); } } }
-
局部内部类
-
Outer
public class Outer { public void method(){ //局部内部类 class Inner{ } } } //一个Java类中可以由多个class类,但只能由一个public class class A{ }
-
匿名内部类
-
Outer
public class Outer { public static void main(String[] args) { //没有名字初始化类,不用将实列保存在变量中 new Apple().eat(); } } class Apple{ public void eat(){ System.out.println("吃"); } }