面向对象编程(OOP)【感谢狂神说JAVA】
2021/6/3 20:21:11
本文主要是介绍面向对象编程(OOP)【感谢狂神说JAVA】,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
面向对象编程(OOP)
Java的核心思想就是OOP(Object-Oriented Programming)
OOP的概念
-
面向过程:
- 步骤清晰简单,第一步做什么,第二步做什么……
- 面向过程适合处理一些比较简单的问题
-
面向对象:
- 物以类聚,分类的思维模式。
- 首先考虑解决问题需要哪些分类
- 对这些分类进行单独思考
- 对某个分类下的细节进行面向过程的思索
- 面向对象适合处理复杂的问题,适合处理需要多人协作的问题
- 也就是说,面向对象实际上是对复杂的问题宏观把控、从整体分析,用面向对象的思路去分析整个系统,然后在微观操作中,用面向过程的思路去解决
- 物以类聚,分类的思维模式。
-
面向对象的本质:以类的方式组织代码。以对象的形式组织(封装)数据
-
抽象:把一个系统的共同特点抽象抽取出来
【编程思想,持续学习,茅塞顿开!多实践,多测试大脑中的想法】
-
三大特征:
- 封装
- 继承
- 多态:同一个事物的多种形态
-
从认识论的角度理解:先有对象,后有类【从很多对象中抽象出来的特征,形成类】。对象,是具体的事物。类,是抽象的,是对对象的抽象。
-
从代码运行的角度考虑:先有类,后有对象【先设计一个类,再生成对象,用new关键字】。类是对象的模板。
方法回顾
- 一个真正的程序中,只有一个main方法
- void方法中,可以不返回结果,也可以只写return
- break结束循环,return结束方法
- 一般一个完整的方法都会有异常处理
// 读文件时抛出输入输出异常 public void readFile(String file) throws IOException{ }
方法调用
-
静态方法(static)和非静态方法
// 对于静态方法 Student.say(); //类名.方法名() // 对于非静态方法 // 首先,将类实例化,通过new关键字 new.Student.say(); // new.类名.方法名() // 常规写法 Student student = new Student(); // 对象类型 对象名=对象值 student.say();
- 在同一个类中
- 静态方法可以调用静态方法
- 非静态方法可以调用静态方法
- 非静态方法可以调用非静态方法
- 但是静态方法不能调用非静态方法。因为static方法是和类同时加载的,它一直存在,而非静态方法只有在类实例化之后才存在。
- 在同一个类中
-
实际参数和形式参数
- 形参和实参的类型要对应
-
值传递和引用传递
- java中都是值传递
- Java中传递对象时,形似引用传递,因为它传递了它的地址,而地址中的内容被改变,但是地址并不改变。所以它本质还是值传递
- 值传递:形参和实参在存储空间上是独立的两个变量;引用传递:实参和形参在内存上指向同一个地址
-
this关键词:代表当前的类或者当前这个对象
类与对象
-
类:
-
一种抽象的数据类型。对某一类事物的整体描述/定义,并不代表某一个具体的事物
-
Person类、Pet类等,描述/定义某一类具体事物所具备的特点
-
一个类中,只存在两种内容
public class Student{ // 属性:字段 String name; int age; // 方法 public void study(){ System.out.println(this.name+"is studying!"); } }
-
-
对象:
- 抽象概念的具体实例
- 张三是Person类的具体实例,旺财是Pet类的具体实例
- 能够体现出特点,展现出功能的是具体的实例,而不是抽象的概念。因此需要将类实例化为对象。
- 引用类型:除了8个基本类型都是引用类型
- 对象是通过引用来操作的:栈 -> 堆(里面保存地址)
创建与初始化对象
【规范】:在写程序时,不要在每个类中都写main方法。一个程序中只有一个main方法,可以定义一个大的启动类(可以叫Application类),并把它放到一边,便于我们随时启动测试
public class Application{ public static void main(String[] args){ // 类:抽象的,先实例化。实例化后会返回一个自己的对象 // student对象就是一个Student类的具体实例 // 这里的Student()就是构造方法 Student student = new Student(); // 这就叫:以对象的形式封装数据 student.name = "xiaoming"; student.age = 3; } }
- 使用new关键字创建对象
- 用创建出的对象+"."来进行调用
- 在使用new关键字时,除了分配内存空间之外,还会给创建好的对象,进行默认的初始化以及对类中构成器的调用
- 类中的构成器,也叫做构造方法。是进行创建对象的时候必须要调用的(即使用new关键字,必须要有构造器,它的本质就是调用构造器)
- 构成器的特点:
- 必须和类的名字相同
- 必须没有返回类型,也不能写void
- 构成器的应用:
- 初始化一些信息
- 有参构造:一旦定义了有参构造,无参构造就必须显式定义(方法重载)
- 快速生成构造器:alt + insert
- 构成器的特点:
public class Student{ // 属性:字段Field 成员变量 /* 初始值: 数字:0 0.0 char: u0000 boolean: false 引用:null */ String name; int age; // 方法 // 一个类即使什么也不写,也存在一个构造方法 // 显式定义构造方法 public Student(){ this.name = "xiaoming"; } // 有参构造 publiv Student(String name){ this.name = name; } public void study(){ System.out.println(this.name+"is studying!"); }
内存分析
封装
-
程序设计的目标:高内聚、低耦合,就是说类内部的数据操作细节自己完成,不允许外部干涉;仅暴露少量的方法给外部使用
-
定义:就是数据的隐藏。通常应该禁止直接访问一个对象中的数据的实际表示,而应该通过操作接口来访问。
-
做法:属性私有(private),get/set
-
意义:
- 提高程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口(getXxx()、setXxx())
- 系统可维护性增加
-
【规范】
// 启动类 public class Application { public static void main(String[] args) { Student student = new Student(); // 属性是私有(private)的无法直接用student.name这样的方式进行调用 // 公有(public)的可以用student.name这样的方式进行调用 // 【对于外部类来说,内部类依然可以调用】 s s = new s(); int name = s.name; student.setName("xiaoming"); System.out.println(student.getName()); } public static class s{ private int name; } } public class Student { //属性私有 private String name; private int age; private char gender; // 提供一些可以操作这个属性的方法:public的get和set方法。也就是接口方法 // get方法,获取这个数据 public String getName() { return name; } // set方法,给这个数据设置值 public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { // 可以在set中加入条件判断 if (age > 120 || age < 0){ this.age = 3; }else{ this.age = age; } } public char getGender() { return gender; } public void setGender(char gender) { this.gender = gender; } }
继承
-
定义:本质是对某一批类的抽象,从而实现对现实世界更好的建模
- 继承是类和类之间的一种关系。除此之外还有依赖、组合、聚合等
- 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends表示
- 子类和父类之间,从意义上讲应该具有"is a"的关系
-
方法:使用extends关键字。子类是父类的扩展
-
功能:
- 子类可以继承父类的方法和属性,拥有父类的全部公有(public)方法和属性【对于私有(private)的属性,可以使用get、set方法,封装的思想】
// 学生 is 人: 派生类(父类),子类 public class Person { public Person(){ System.out.println("Person的无参构造"); } public int money = 10; protected String name = "xiaoli"; public void say(){} public void print(){ System.out.println("person"); } } public class Student extends Person{} public class Teacher extends Person{ public Teacher(){ super(); // 继承中的这一行代码被隐藏了。并且这一代码必须放在子类构造器的第一行 System.out.println("Teacher的无参构造"); } private String name = "xiaoming"; public void print(){ System.out.println("teacher"); } public void test(String name){ System.out.println(name); // xiaofang System.out.println(this.name); // xiaoming System.out.println(super.name); // xiaoli } public void test1(){ print(); // teacher this.print(); // teacher super.print(); // person } } public class Application { public static void main(String[] args) { Teacher teacher = new Teacher(); // 输出:Person的无参构造\nTeacher的无参构造 teacher.say(); teacher.test("xiaofang"); } }
-
Java中只有单继承,没有多继承(一个儿子只能有一个爸爸,extends后只能有一个类,但是可以一个爸爸可以有多个儿子,多个类extends同一个类)
-
在java中,所有类都默认继承object类
-
super,与this相对应,表示父类。只有存在继承才可以使用
-
注意:
-
super()调用父类的构造方法必须放在子类构造器或者子类方法的第一行
-
this()和super()不能同时存在,因为都要求放在第一行
-
若父类中存在有参构造没有无参构造,则不能调用super(),并且要写上super(参数),否则会报错
-
-
方法重写
-
定义:子类重写父类的方法
-
要求:子类的方法与父类的方法必须要一致,但方法体不同
-
方法:alt + insert -> override
-
为什么需要重写:
- 父类的功能,子类不一定需要,
- 父类的功能不一定满足需求
-
注意:
- 重写只针对方法,与属性无关
- 重写只针对非静态方法
- 对于static方法,方法属于类,它不属于实例
- 对于非static方法,子类的重写会重写父类的方法
- 静态方法与非静态方法差别很大
- 重写的修饰符:范围可以扩大,但不能缩小,eg:父类是private,子类是Default
【修饰符的范围:public > Protected > Default > private】
- 重写的抛出异常:范围可以被缩小,但不能扩大。eg: ClassNotFoundException --> Exception
- final修饰的是常量,不能被改变
IDEA中,代码左边栏中的O代表重写(overwrite method)
public class B{ public static void test(){ System.out.println("B"); } public static void testAgain(){ System.out.println("B"); } } public class A extends B{ public static void test(){ System.out.println("A"); } // @Override表示重写 @Override // 注解:有功能的注释 public static void testAgain(){ System.out.println("A"); } } public class Application{ public static void main(String[] args){ // 方法的调用只和左边定义的数据类型有关 // 对于static方法,方法属于类,它不属于实例 A a = new A(); B b = new A(); // 我理解的是子类的构造器会首先运行父类的构造器,因此可以如此定义。 // 并且对于静态方法,可以直接通过类来访问,因此输出结果为A a.test(); // A // 父类的引用指向子类 b.test(); // B // 对于非static方法,子类的重写会重写父类的方法 a.testAgain(); // A b.testAgain(); // A } }
重写与重载的区别:
- 重载是在同一个类中,方法名相同,参数列表不同
- 重写需要有继承关系,子类重写父类的方法
- 重写的方法名必须相同,参数列表也必须相同
多态
- 定义:同一方法可以根据发送对象的不同而采用多种不同的行为方式
- 一个对象的实际类型是确定的,但是它可以指向的引用类型不确定(即下方代码中的te1, te2, te3调用say方法时产生不一样的行为,这就是多态)
- 应用场景:通过多态可以让代码的可扩展性变得更强,让代码更灵活
public class Person { public void say(){ System.out.println("i am a person"); } } public class Teacher extends Person{ @Override public void say() { super.say(); System.out.println("i am a teacher"); } } public class Application { public static void main(String[] args) { Teacher te1 = new Teacher(); // 父类的引用指向子类 Person te2 = new Teacher(); Object te3 = new Teacher(); // 对象执行哪些方法,主要看对象左边的类型,和右边关系不大 // 子类可以调用自己或者继承自父类的方法 te2.say(); // 方法被重写 te1.say(); // 父类可以指向之类,但是不能调用子类独有的方法 // te3.say(); // 报错,因为Object类中没有这一方法 ((Teacher) te3).say(); // (Teacher) te3强制类型转换,可以由父类转换成子类(高到低) } }
-
条件:
- 需要有继承关系
- 方法需要被重写
- 父类的引用指向子类对象 Father f1 = new Son()
-
注意:
- 多态是方法的多态,与属性无关
- 父类和子类必须有联系,否则类型转换异常 ClassCastException
- 不能被重写的方法(static final private)无法实现多态
instanceof
用于判断两个变量之间有无父子关系
public class Person { public void run(){ System.out.println("run"); } } public class Teacher extends Person{ public void go(){ System.out.println("go"); } } public class Student extends Person{} public class Application { // Object > String // Object > Person > Teacher // Object > Person > Student public static void main(String[] args) { Object te3 = new Teacher(); System.out.println(te3 instanceof Teacher); // true System.out.println(te3 instanceof Person); // true System.out.println(te3 instanceof Object); // true System.out.println(te3 instanceof Student); // false System.out.println(te3 instanceof String); // false Person te2 = new Teacher(); System.out.println(te2 instanceof Teacher); // true System.out.println(te2 instanceof Person); // true System.out.println(te2 instanceof Object); // true System.out.println(te2 instanceof Student); // false // System.out.println(te2 instanceof String); // 编译错误 Teacher te1 = new Teacher(); System.out.println(te1 instanceof Teacher); // true System.out.println(te1 instanceof Person); // true System.out.println(te1 instanceof Object); // true // System.out.println(te1 instanceof Student); // 编译错误 // System.out.println(te1 instanceof String); // 编译错误 // 类型之间的转换 // 基本类型转换:高转低需要强转,低转高则不需要 // 类之间的转换(父类与子类):高转低需要强转,低转高则不需要(同理) // 高 低 Person te = new Teacher(); // 子类转父类可能会丢失一些方法 // te.go(); // 报错,父类无法执行子类中的方法,因此要进行转换 Teacher t = new (Teacher) te; t.go(); ((Teacher) te).go(); } }
类转换的作用:方便方法的调用,减少重复的代码。简洁
static关键字
使用总结:
public class Application { { // 匿名代码块,在构造器之前执行,无法主动调用 } static { // 静态代码块,在构造器之前执行,只执行一次(在匿名代码块之前执行) // 应用:赋初值 } private static int age; private double score; // 非静态方法可以调用静态方法 public void run(){ go(); } public static void go(){ // run(); // 报错 } public static void main(String[] args) { Application stu = new Application(); System.out.println(stu.age); System.out.println(stu.score); System.out.println(Application.age); // System.out.println(Application.score); // 报错 stu.run(); stu.go(); // Application.run(); // 报错 Application.go(); go(); // run(); // 报错 } }
其他应用:
// 静态导入包 import static java.lang.Math; // import static java.lang.Math.random; public class Application { public static void main(String[] args) { // System.out.println(Math.random()); // 通过静态导入包来进行替代写法 System.out.println(random()); } }
final修饰类
不能被继承(inherent)
public final class Person{} public class Student extends Person{} // 报错
抽象类
-
abstract修饰符。
- 用来修饰类,就是抽象类;
- 用来修饰方法,则为抽象方法
-
抽象类中可以没有抽象方法,但是存在抽象方法的类一定要声明为抽象类
-
抽象类,不能使用new关键字来创建对象,它的应用场景在于被子类继承
-
抽象方法,只有方法的声明,没有方法的实现,它的应用场景在于被子类实现
-
子类继承抽象类,那么就被需要实现抽象类中没有实现的抽象方法,否则该子类也要被声明为抽象类
-
抽象类可以有构造方法,只是不能直接创建抽象类的实例对象而已。在继承了抽象类的子类中通过super()或super(参数列表)调用抽象类中的构造方法。
-
应用:为后续的操作做一个规范,把后面的一些公有属性抽象出来,避免反复进行操作。总之,就是为了提高开发效率,提高可扩展性
【理解:抽象类就是提供一个框架,让你后续的操作沿着框架进行】
public abstract class Person { // 作为一个约束,让别人帮助实现 public abstract void doSomething(); public void done(){} } public class Teacher extends Person{ @Override public void doSomething() {} }
接口(最能体现OO精髓)
-
存在的意义:类只能单继承,因此有一定的局限性,为了克服这种局限性,使用多继承的接口
-
定义:提供一种规范,类似于现实世界中”如果你是……你必须……“的思想。
-
本质:一种契约,类似法律,大家都要遵守并执行
-
普通类、抽象类、接口的区别
- 普通类:只有具体实现(普通方法)
- 抽象类:既存在规范(抽象方法)又存在具体实现
- 接口:仅有规范!【约束和实现分离】
-
声明接口:用关键字interface
public interface Person { // 作为一个约束,让别人帮助实现,接口必须有实现类 void doSomething(); // 默认隐藏public abstract void go(String name); } public interface Student{ // 一般不定义常量 String name = "xiaoming"; // 常量默认隐藏public static final void cry(); } // 类通过implements实现接口 public class Teacher implements Person,Student{ @Override public void doSomething() {} @Override public void go(String name) { } @Override public void cry() { } }
- 作用:
- 作为约束,提供一个架构(架构师)
- 定义一些方法,让不同的人可以通过不同思路实现
- 接口不能被实例化(接口中不存在构造方法)
- 可以实现多个接口(多继承)
- 必须重写接口中的方法
内部类(扩展,先来见一下)
- 定义:在一个类的内部定义另一个类
- 包括:
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
- lambda表达式
public class Out { private int id; public void out(){ System.out.println("this is out"); // 局部内部类: 在方法中 class inner2{} } class Inner1{ public void in(){ System.out.println("this is in"); } // 成员内部类 // 获取外部类的私有属性和方法 // 同样的,若内部类为static(静态内部类)则无法获得外部类的属性和方法 public void getID(){ out(); System.out.println(id); } } } // 一个java中可以有多个class,但只能有一个public class class A{ } public class Application { public static void main(String[] args) { Out out = new Out(); // 通过这个外部类来实例化内部类 Out.Inner1 inner = out.new Inner1(); inner.in(); inner.getID(); // 匿名内部类 new Apple().eat(); Service hello = new Service() { @Override public void hi() { System.out.println("hello"); } }; } } class Apple{ public void eat(){ System.out.println("good"); } } interface Service{ void hi(); }
这篇关于面向对象编程(OOP)【感谢狂神说JAVA】的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-26Mybatis官方生成器资料详解与应用教程
- 2024-11-26Mybatis一级缓存资料详解与实战教程
- 2024-11-26Mybatis一级缓存资料详解:新手快速入门
- 2024-11-26SpringBoot3+JDK17搭建后端资料详尽教程
- 2024-11-26Springboot单体架构搭建资料:新手入门教程
- 2024-11-26Springboot单体架构搭建资料详解与实战教程
- 2024-11-26Springboot框架资料:新手入门教程
- 2024-11-26Springboot企业级开发资料入门教程
- 2024-11-26SpringBoot企业级开发资料详解与实战教程
- 2024-11-26Springboot微服务资料:新手入门全攻略