面向过程:第一步做什么,第二步做什么......适合处理一些简单的问题;面向对象:分类的思维,解决一个问题会用到哪些分类,然后对这些类单独思考,最后才是对某个类上的细节进行面向过程编程。
面向对象的本质:以类的方式组织代码,以对象的方式组织(组装)数据
类
类是抽象的,是对一类事物共同点的描述。你可以把“抽象”理解为“抽像”,抽离一类事物中很像的共同点。
对象
对象是某一类中具体的事物。
创建对象
对象的创建和使用
- 必须使用
new关键字创建对象,构造器 - 对象的属性,
对象.属性 - 对象的方法,
对象.方法
使用关键字new创建对象
- 给对象分配内存空间,
new出来的对象都在堆中 - 给创建好的对象进行默认的初始化
- 对类中的构造器的调用
// Student.java
public class Student {
// 属性
String name; // null 默认的初始化
int age; // 0
// 方法
public void study () {
System.out.println(this.name+"在学习");
}
}
// Application.java
public class Application {
public static void main(String[] args) {
// 使用关键字new创建Student类型的对象student(保存的是对象的引用)
Student student = new Student();
student.name = "小明";
student.study();
}
}
构造器
类中的构造器也叫构造方法,是在进行创建对象的时候必须要调用的。
构造器的特点
- 1、必须和类的名字相同
- 2、必须没有返回值类型,也不能写
void构造器的作用 - 1、使用
new关键字,必须要有构造器,new本质是在调用构造器 - 2、用来初始化属性的值
// Person.java
class Person {}
// Application.java
class Application {
public static void main (String[] args) {
Person person = new Person(); // new的本质是在调用构造器
}
}
用idea打开out文件夹下的Person.class文件,发现编译后的字节码文件默认会加上一个构造方法。
一个类即使什么都不写,它也会存在一个方法,无参构造。
// Person.class
public class Person {
public Person() {
}
}
一旦定义了一个有参的构造器,无参构造器就必须显式定义,否则无法用 new 去调用无参构造,会报错。
无参构造和有参构造相当于是方法的重载。
// Person.java
class Person {
String name;
// 无参构造
public Person () {
}
// 有参构造
public Person (String name) {
this.name = name;
}
}
// Application.java
class Application {
public static void main (String[] args) {
// 调用无参构造
Person person = new Person();
// 调用有参构造,初始化值
Person person = new Person("张三");
}
}
idea快捷键生成构造器:Alt + Ins
创建对象内存大致分析
// Pet.java
public class Pet {
public String name;
public int age;
public void shout () {
System.out.println(this.name + "叫了一声.");
}
}
// Application.java
public class Application {
public static void main(String[] args) {
Pet dog = new Pet();
dog.name = "旺财";
dog.age = 3;
dog.shout(); // 旺财叫了一声!
System.out.println(person.name);
Pet cat = new Pet();
cat.name = "喵喵";
cat.age = 2;
cat.shout();
}
}
面向对象的三大特性
封装、继承、多态
封装
设计程序要讲究:高内聚,低耦合。高内聚:类的内部数据操作细节由自己来完成,不允许外部干涉。低耦合:仅暴露少量的方法供外部使用。
属性私有,get/set。属性是私有的,只能用通过get来获取,用set来改变属性的值
属性为什么要私有?
试想:如果编写一个类来描述人类,人类有age属性,如果age不是私有的,可以随意给age设置值,负数的年龄、5000岁的年龄,会对系统造成破坏,为了防止外部随意篡改内部的属性,可将其修饰为私有属性,通过setter/getter来设置和获取,并在setter中对修改的age进行合法性逻辑的把控。
// Student.java
public class Student {
// 属性私有
private String name;
private int age;
private char gender;
// getter / setter 一般不会重载
public void setAge(int age) {
if (age > 120 || age < 0) { // 不允许外部对私有的数据进行非法的干涉
this.age = 3;
} else {
this.age = age;
}
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
// Application.java
public class Application {
public static void main(String[] args) {
Student student = new Student();
// student.name = "小明";
// student.study();
student.setAge(130);
student.setName("小明");
System.out.println(student.getName()); // 小明
System.out.println(student.getAge()); // 3
}
}
idea快捷键快速生成getter/setter:Alt + Ins
封装的意义:
- 提高代码的安全性,保护数据
- 隐藏代码的实现细节
- 增加系统的可维护性
继承
继承概述
继承可以理解为,对类的再一次分类,
Student类、Teacher类等等都可以被划分在Person类之下。
继承的本质是对某一批类的抽象
使用extends关键字,子类是父类的拓展。
Java中类只有单继承,没有多继承! 一个子类只能继承自一个父类,而一个父类可以让多个子类继承。
除了继承之外,类与类之间的关系还有依赖、组合、聚合等。
子类和父类之间,可以理解为,子类 "是一个" 父类,Teacher是一个Person。
// Application.java
/**
* @author admin
* @description 继承
*/
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.say();
System.out.println(student.money);
// System.out.println(student.age); // 继承跟封装有什么关系,age在父类是私有属性,被封装了(被private修饰的属性和方法,不能被子类继承)
// Ctrl + h 把鼠标光标放在类的括号中间,按 ctrl + h 显示类与类继承的树形关系
student.toString(); // 所有的类都在Object类之下
}
}
// Person.java
public class Person {
// public protected default private
public int money = 10_0000_0000;
private int age = 23;
public void say () {
System.out.println("说了一句话");
}
}
// Student.java
public class Student extends Person {
}
super 和 this
使用super.属性和this.属性使用父类和子类的属性
使用super.方法和this.方法使用父类和子类的方法
使用super()和this()调用父类和子类的无参构造函数,如果传参了就是调用有参的构造函数。
当父类和子类有相同名字的属性,在子类中可以用super.父类的属性使用父类的属性
当子类重写了父类的方法,子类可以用super.父类的方法调用父类的方法
注意点:
static修饰的方法中访问不到this和super,因为静态的方法与类一起诞生,此时并不存在this和superthis();和super();只能在构造器中第一行书写,两者只能存在一个。this();最多能被调用n-1次(n为构造器个数),不能在构造器中调用自己,不能成环。super();父类的构造器(不确定是否有参)最少被调用一次。当父类的有参被定义时,在子类构造器中未调用父类有参的构造器的前提下,父类无参必须显示定义。当子类的构造器中没有书写super(有参)和this(无参、有参),会默认调用父类无参的构造器super()
// Application
public class Application {
public static void main(String[] args) {
Student student = new Student();
/*
Person无参构造执行了
Student无参构造执行了
*/
// student.test("很狂的神");
student.test1();
}
}
// Student.java
public class Student extends Person {
private String name = "狂神";
// 当父类无参构造不存在,子类连无参构造都不能写
public Student() {
// 隐藏代码 默认调用了父类的无参构造,当super调用的是有参构造,无参构造就不会被调用
// super(); // 必须在构造器的第一行,不写参数是调用父类的无参
// this("a", "b"); // 也必须在构造器的第一行, 不能在无参构造器里调用无参构造,递归
// 因此this(); 和 super(); 不能同时存在
System.out.println("Student无参构造执行了");
}
public Student(String name, String name1) {
super(name);
this.name = name1;
}
public void test (String name) {
System.out.println(name); // 很狂的神
System.out.println(this.name); // 狂神
System.out.println(super.name); // kuangshen
}
public void test1 () {
// 都是调用Student类的print方法
print(); // Student
this.print(); // Student 建议写全
// ----------------------
super.print(); // Person
}
public void print () {
System.out.println("Student");
}
}
// Person.java
public class Person {
protected String name = "kuangshen";
public Person() {
System.out.println("Person无参构造执行了");
}
public Person(String name) {
this.name = name;
}
public void print () { // 如果是private,在子类中super.print()也不能调用这个方法
System.out.println("Person");
}
}
多态
方法的重写
重写只针对方法。属性没有重写!
重写的前提是继承,子类重写父类的方法!
-
- 子父类具有同名同参数的方法
-
- 修饰符:范围相对父类可以扩大,但不能缩小:
public>protected>default>private
- 修饰符:范围相对父类可以扩大,但不能缩小:
-
- 返回值类型必须相同(注:如果是父类方法返回值是A类,子类重写的方法返回值可以是A类或者A类的子类)
-
- 抛出的异常:范围,可以被缩小,但是不能扩大。
ClassNotFoundException(小)Exception(大)
- 抛出的异常:范围,可以被缩小,但是不能扩大。
-
- 方法体不一样(一样的话,重写没有意义)
- 方法体不一样(一样的话,重写没有意义)
为什么要重写?
父类的功能,子类不一定需要,或者不一定满足子类的需求。
不能被重写的方法:
-
private修饰的方法
-
static修饰的方法,不叫重写,被static修饰的方法(类.方法())与实例调用方法(实例.方法())的方式都不同:子类和父类中同名同参数的方法要么都声明为非static(考虑重写),要么都声明为static的(不是重写)。
-
final修饰的方法
// Application.java
public class Application {
public static void main(String[] args) {
Teacher teacher = new Teacher();
Person person = new Teacher();
// teacher.add(); // Teacher类
// person.add(); // Person类
teacher.add(); // Teacher类
person.add(); // Teacher类
}
}
// Teacher.java
public class Teacher extends Person {
// public static void add () {
// System.out.println("Teacher类");
// }
public void add () {
System.out.println("Teacher类");
}
}
// Person.java
public class Person {
// public static void add () {
// System.out.println("Person类");
// }
public void add () {
System.out.println("Person类");
}
}
idea会对方法的重写做标记
多态
多态的前提:1、类的继承关系;2、方法的重写。
- 对象的多态:父类的引用指向子类的对象。
// Man、Woman extends Person
// Person eat() walk()
// Man eat() walk() earnMoney()
// Woman eat() walk() shopping()
// 多态性
Person p2 = new Man();
Person p3 = new Woman();
p2.eat(); // 男人大口大口地吃饭!
p3.walk(); // 女人窈窕地走路!
// p2.earnMoney(); // 报错,只能调用父类和子类中重写父类的方法。
- 多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法
编译的时候,发现Person类有eat和walk方法(Ctrl + 鼠标左键 进到父类的方法中),没有earnMoney方法(报错);执行的时候,发现执行的是子类重写父类的方法
总结:编译看左边,运行看右边。
// Son extends Father 子类重写了父类的print方法 子类独有一个talk方法
Father father = new Son();
father.print(); // 编译时,father有print方法,可以调用
// father.talk(); // 不可以调用,编译报错
((Student)father).talk(); // 类型强转后,可以调用
多态注意点:
- 父类和子类,有联系,一定要有父子之间的关系才能进行类型转换,
String s = new Student();这样是不行的。类型转换异常:ClassCastException - 多态的使用,只体现在方法上(方法编译看左边,运行看右边),属性是不行的(属性编译和运行都是看左边)。
多态的必要性
- 如果没有多态,则必须要写很多重载的方法
class Order {
public void method(Object obj) { // obj = new String();
// ...
}
}
再例如:java.lang.Object上的equals(Object obj)方法
String str = "1";
str.equals("1");
- 没有多态,抽象类和接口将没有意义。
多态在内存中的样子
向上转型和向下转型
怎么才能调用子类特有的属性和方法?
子类可以非常自然地转换成父类,称为多态(向上转型);
Person p = new Man();
但是父类转换成子类则需要强制转换(也称为向下转型)。
Man man = (Man)(new Person()); // 编译时通过,运行时不通过
Person p1 = new Man();
Man m1 = (Man)p1;
向下转型在多态的前提下,再向下转型为之前的类型。
向下转型不能乱转,否则运行时会报错ClassCastException
instanceof
语法:实例对象 instanceof 类
当对象是右边类或子类所创建对象时,返回true;否则,返回false。
实例对象跟类风马牛不相及的编译报错。
// Teacher extends Person
// Student extends Person
/*
Object Person Teacher
Object Person Student
Object String
*/
public static void main(String[] args) {
Student student = new Student();
System.out.println(student instanceof Student); // true
Object object = new Student();
Person person = new Student();
System.out.println(object instanceof Object); // true
System.out.println(object instanceof Person); // true
System.out.println(object instanceof Student); // true
System.out.println(object instanceof String); // false
System.out.println(person instanceof String); // 实例对象跟类风马牛不相及的编译报错
}
抽象类
public abstract class Action {
public abstract void doSomething(); // 用在方法上,没有方法体
}
public class B extends Action{
@Override
public void doSomething() {
System.out.println("实现抽象类");
}
}