什么是面向对象
-
java
的核心思想就是面向对象 -
两种编程思想
-
面向过程思想(线性思维)
- 步骤清晰简单,第一步做什么,第二步做什么
- 面向过程适合处理一些较为简单的问题
-
面向对象思想
- 是一种分类的思维模式,思考解决问题需要哪些分类,然后对这些分类进行单独思考,最后,才对某个分类下的细节进行面向对象的思索.每个分类作为一个类(类是抽象的,例如人这一普遍概念,使用时,再把人这一普遍概念的共性对应到具体某一个人身上).
- 面向对象适合处理复杂问题,适合多人协作,每人负责问题的一个分类的解决(类似于一个大任务分成各类小任务)
- 注意:对于描述复杂事物,为了从宏观上把握,从整体上合理分析,需要使用面向对象的思路来分析整个系统.但是,具体到微观操作,仍然需要面向过程的思路去处理.
-
面向过程思想(线性思维)
-
面向对象编程
(Object-Oriented Programming,OOP)
- 面向对象编程的本质:以类的方式组织代码,以对象的方式组织(封装)数据
- 抽象:把一类事物中的共性提取出来(例如学生都有学号,学号就是共性)
-
面向对象三大特性
- 封装:把数据包装起来,通过特定的接口进行访问
- 继承:子类会默认继承父类当中已有的东西
- 多态:就是同一个事物有多个形态,例如人分为白人黑人,人都有跑这一个动作,但黑人跑的比白人快
- 从认识论的角度是先有对象后有类.对象是具体的事物;类是抽象的,是对对象的抽象
- 从代码运行的角度考虑是先有类后有对象,类是对象的模板
方法回顾
public class Hello {
public static void main(String[] args) {
}
/*
修饰符 返回值类型 方法名(..){方法体}
*/
// return 结束方法,返回一个结果
// break只结束循环,后面语句可以继续执行
public String sayHello() {
return "Hello,world";
}
public void hello() {
return;
}
public int max(int a, int b) {
return a > b ? a : b; //三元运算符
}
}
package Demo;
// 学生类
public class Student {
// 非静态方法
public void say() {
System.out.println("学生说话了");
}
}
package Demo;
public class Demo01 {
public static void main(String[] args) {
// 实例化要调用的类
// 对象类型 对象名=对象值;
Student student = new Student();
student.say();
}
}
package Demo;
public class Demo01 {
// 以下这种情况,b可以调用a,但a不可以调用b
// 静态方法static和类一起加载
public static void a() {
}
// 非静态方法需要在类实例化(new)之后才存在
public void b() {
}
}
package Demo;
// 值传递
// java都是值传递
public class Demo01 {
public static void main(String[] args) {
int a = 1;
System.out.println(a);
Demo01 demo = new Demo01();
demo.change(a);
System.out.println(a);
}
// 未返回任何值
public void change(int a) {
a = 10;
}
}
package Demo;
// 引用传递,操作的是对象,本质还是值传递
public class Demo01 {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name);
Demo01 demo = new Demo01();
demo.change(person);
System.out.println(person.name);
}
// 未返回任何值
public void change(Person person) {
// person是一个对象,而不是形参,person对象指向Person person = new Person();这是一个具体的人,可以改变名字(属性)
person.name = "我的名字";
}
}
// 一个类中只能有一个public类
// 定义一个Person类,有一个属性:name
class Person {
String name; // 默认值为null
}
类与对象的创建
类与对象的关系
-
类是一种抽象的数据类型,是对某一类事物的整体描述/定义,但是不能代表某一个具体的事物
- 例如:动物,植物,手机,电脑
- 类是用来描述/定义某一类具体的事物应该具备的特点和行为
-
对象是抽象概念的具体实例
- 张三就是人的一个具体实例,张三家里的旺财就是狗的一个具体实例
- 能够展现出特点,展现出功能是具体的实例,而不是一个抽象的概念
创建与初始化对象
-
使用
new
关键字创建对象 -
使用
new
关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及类中的构造器的调用 -
类的构造器也称为构造方法,是在进行创建对象的时候必须要调用,并且构造器有以下两个特点
-
必须和类的名字相同
-
必须没有返回类型,也不能写
void
-
-
构造器必须要掌握
package Demo;
// 学生类,一个类里面只能有属性和方法两种东西
public class Student {
// 属性
String name;
int age;
// 方法
// this指的是当前类
public void study() {
System.out.println(this.name + "在学习");
}
}
package Demo;
// 一个项目应该只存在一个main方法
public class Application {
public static void main(String[] args) {
// 类,抽象的,实例化
// 类实例化后会返回一个自己的对象
// student对象就是Student类的具体实例
Student student1 = new Student();
Student student2 = new Student();
student1.name = "xiaoming";
student1.age = 2;
student2.name = "xiaohong";
student2.age = 2;
System.out.println(student1.name);
System.out.println(student2.age);
}
}
构造器详解
-
一个类即使什么都不写,依然可以
new
实例化,因为会自动存在一个构造方法 -
类的构造器也称为构造方法,是在进行创建对象的时候必须要调用,并且构造器有以下两个特点
-
必须和类的名字相同
-
必须没有返回类型,也不能写
void
-
// 例如一个这样的类
class Student {}
// 编译后会变成如下样子,会自动添加一个方法
// 这被称为无参构造器,可以帮忙初始化一些东西
class Student {
// 实例化初始值
public Student(){
}
}
package Demo;
// 学生类,一个类里面只能有属性和方法两种东西
public class Student {
// 属性
String name;
int age;
// 构造器.不写会自动生成,这里手写一下,同时初始化一下值
public Student() {
this.name = "你的名字";
}
// 方法
// this指的是当前类
public void study() {
System.out.println(this.name + "在学习");
}
}
package Demo;
// 一个项目应该只存在一个main方法
public class Application {
public static void main(String[] args) {
Student student1 = new Student();
System.out.println(student1.name);
}
}
-
构造器的作用
-
使用
new
关键字,必须要有构造器,因为本质是在调用构造器 - 有参构造:一旦定义了有参构造,无参构造就必须显式定义
-
alt+insert
可以快捷地生成构造器 - 可以定义多个构造器,到时候需要哪个就会自动调用哪个
-
使用
package Demo;
// 学生类,一个类里面只能有属性和方法两种东西
public class Student {
// 属性
String name;
int age;
// 构造器.不写会自动生成,这里手写一下,同时初始化一下值
public Student() {
this.name = "你的名字";
}
// 有参构造一旦定义了有参构造,无参构造就必须显式定义
public Student(String name) {
this.name = name;
}
// 方法
// this指的是当前类
public void study() {
System.out.println(this.name + "在学习");
}
}
package Demo;
// 一个项目应该只存在一个main方法
public class Application {
public static void main(String[] args) {
Student student1 = new Student("xueyan");
System.out.println(student1.name);
}
}
创建对象的内存分析
java
程序运行时主要分以下几步
-
先加载每一个类,同时读取类中的方法和常量,此时会同步加载
static
静态方法(因为static
与类一起加载,所以之后类中可以直接调用) -
将
main
方法放到栈底,因为栈是先进后出,当main
方法出栈的时候可以基本理解为程序运行结束 - 实例化类,每个实例会作为引用变量放到栈中间(注意,这里只是一个引用)
- 每个实例的对象(具体的值,方法等)会存储在堆中,每个实例可以调用静态方法
简单小结类和对象
- 类是一个模板(抽象),对象是一个具体的实例
- 方法的定义和调用
-
对象的引用
- 引用类型:对象是通过引用来操作的(对象引用放在栈当中,具体操作的实际上是堆)
- 基本类型(8种)
-
属性=字段
field
=成员变量(对象当中只有引用和方法),属性会进行默认初始化-
修饰符 属性类型 属性名=属性值
-
-
对象的创建和使用
-
必须使用
new
关键字创建,必须具有构造器(构造器不写编译器会默认生成) - 对象的属性
- 对象的方法
-
必须使用
-
类,只有以下两个东西
- 静态的属性,即属性
- 动态的行为,即方法
封装详解
-
封装:简单理解就像人把东西放在箱子中,要拿东西只能通过固定的口去拿出来
-
该露的露,该藏的藏
- 程序设计追求高内聚,低耦合,高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合就是只暴露少量的方法给外部使用
- 类似于电视:我们只用遥控器就可以操作,显像管什么的不需要了解
-
封装(数据的隐藏)
- 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这被称为信息隐藏
-
记住这句话:属性私有,
get/set
-
封装更多针对属性而言,方法用不到多少封装
-
方法重载,方法名称一致,但输入值或者返回值类型不同,编译器会自动调用适合的方法
-
封装的好处
- 提高程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口
- 系统可维护性增加
package Demo;
// 学生类,一个类里面只能有属性和方法两种东西
public class Student {
// 属性私有
private String name;
private int id;
private char sex;
private int age;
// 提供一些可以操作这个私有属性的方法
// 提供一些public的get/set方法
// get获取数据,set给数据设置值
// alt+insert可以自动生成get/set方法
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
// 通过封装,可以在输入值的时候进行属性判断
public void setAge(int age) {
if (age < 0 || age > 120) {
age = 3;
this.age = age;
} else {
this.age = age;
}
}
public int getAge() {
return this.age;
}
}
package Demo;
// 一个项目应该只存在一个main方法
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.setName("你的名字"); // 设置名字
System.out.println(student.getName()); // 获取名字
student.setAge(140);
System.out.println(student.getAge());
}
}
继承
- 继承的本质是对一批类进行抽象(类太多了的情况也需要进行抽象),例如人的基因虽然每个人或多或少不同,但基因中都表现了人都有手有脚
- 继承是类与类之间的一种关系.除此之外,类与类之间的关系还有依赖,组合,聚合等
-
继承分子类(扩展类)和父类(基类),子类继承父类用
extends
表示- 父类:被继承的类,例如人
- 子类(扩展类):例如学生是人,继承了人的所有共性,学生就是子类.子类可以继承父类的所有方法
-
extends
的意思是扩展,子类是父类的扩展 -
java
中只有单继承,没有多继承,即一个类只能继承单个类,例如基因上说,儿子只能有一个爸爸,一个爸爸却可以有多个儿子 -
object
:在java
中,所有类都直接或者间接继承Object
类
package Demo;
// 在java中,所有类都直接或者间接继承`Object`类
public class Person {
private int money = 10_0000_0000;
public void say() {
System.out.println("人说了一句话");
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
package Demo;
// 子类继承父类,就会拥有父类的属性和全部方法
// 注意,如果父类使用了private修饰符,则子类就无法继承,一般属性都是私有的,公共的都是方法
public class Student extends Person {}
package Demo;
// 一个项目应该只存在一个main方法
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.say(); // say方法在Student中没有定义,但仍然可以调用父类
System.out.println(student.getMoney());
}
}
Super
package Demo;
// 一个项目应该只存在一个main方法
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.test("名字");
}
}
package Demo;
// 在java中,所有类都直接或者间接继承`Object`类
public class Person {
public Person(String name) {
System.out.println("Person无参执行了");
}
protected String name = "xueyan";
}
package Demo;
// 子类继承父类,就会拥有父类的属性和全部方法
// 注意,如果父类使用了private修饰符,则子类就无法继承,一般属性都是私有的,公共的都是方法
public class Student extends Person {
private String name = "狂神";
// 执行子类构造器时,会先执行父类的构造器
public Student(){
// 隐藏代码,调用了父类的构造器
super("name"); // 显式调用时,必须在第一行
System.out.println("Student无参执行了");
}
// super代表调用父类方法,但如果父类中方法为private,则不能用super调用
public void test(String name) {
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);
}
}
super
注意点:
-
super
调用父类的构造方法,必须在构造方法第一个 -
super
必须只能出现在子类的方法或者构造方法中 -
super
和this
不能同时调用构造方法
super
和this
区别
-
代表对象不同
-
super
代表父类对象应用 -
this
本身调用的对象
-
-
前提
-
this
没有继承也可以使用 -
super
只有在继承情况下才可以使用
-
-
构造方法
-
this()
:本类的构造器 -
super()
:父类的构造器
-
方法的重写
-
重写都是方法的重写,与属性无关
-
重写只发生在父子类之间
-
重写只跟非静态公开方法有关,与静态方法没关系
-
使用静态方法时
package Demo; // 重写都是方法的重写,与属性无关 public class A { public static void test() { System.out.println("A=>test()"); } }
package Demo; public class B extends A { public static void test() { System.out.println("B=>test()"); } }
package Demo; // 一个项目应该只存在一个main方法 public class Application { public static void main(String[] args) { B b = new B(); b.test(); // 父类的引用指向了子类,实际上调用了B继承的父类A // 在静态方法时,方法的调用只和等号左边定义的数据类型有关 A a = new B(); a.test(); } }
-
不使用静态方法时
package Demo; // 重写都是方法的重写,与属性无关 public class A { public void test() { System.out.println("A=>test()"); } }
package Demo; public class B extends A { // 重写 @Override // 注解:有功能的注释 public void test() { System.out.println("B=>test()"); } }
package Demo; // 一个项目应该只存在一个main方法 public class Application { public static void main(String[] args) { B b = new B(); b.test(); // 父类的引用指向了子类,实际上调用了B继承的父类A // 在静态方法时,方法的调用只和等号左边定义的数据类型有关 // 非静态方法时,父子类相同名称函数发生的就是重写 A a = new B(); a.test(); } }
-
重写总结:类之间必须有继承关系,子类重写父类的方法
-
方法名必须相同
-
参数列表必须相同
-
修饰符:范围可以扩大但不能缩小(子类修饰符范围可以大于父类修饰符的范围)
public>protected>default>private
-
抛出的异常:范围可以被缩小,但不能扩大(子类的异常范围必须小于父类的异常范围)
-
子类的方法必须要和傅雷一直,但方法体不同
-
-
为什么需要重写
-
父类的功能,子类不一定需要,或者不一定满足.快捷键
alt+insert
-
父类的功能,子类不一定需要,或者不一定满足.快捷键
多态
- 多态:统一方法可以根据发送对象的不同而采取多种行为方式
- 一个对象的实际类型是确定的,但可以指向对象的引用类型有很多(即一个对象实际类型肯定已知,但其引用对象的类型未知)
- 用途:动态编译,一个数据的类型只有在运行时才完全确定,增强程序的可拓展性
-
多态存在的条件
- 类之间存在继承关系
- 子类重写父类的方法
- 父类引用指向子类对象
- 注意:多态是方法的多态,属性没有多态
package Demo;
// 在java中,所有类都直接或者间接继承`Object`类
public class Person {
public void run() {
System.out.println("run");
}
}
package Demo;
// 子类继承父类,就会拥有父类的属性和全部方法
// 注意,如果父类使用了private修饰符,则子类就无法继承,一般属性都是私有的,公共的都是方法
public class Student extends Person {
@Override
public void run() {
System.out.println("son");
}
public void eat() {
System.out.println("eat");
}
}
package Demo;
// 一个项目应该只存在一个main方法
public class Application {
public static void main(String[] args) {
// 一个对象的实际类型是确定的,但可以指向的引用类型不确定
// Student能调用的方法都是自己的或者继承父类的
Student s1 = new Student();
// 下面被称为父类的引用指向子类的类型,实际类型是student,引用的是person
// 可以直观理解为,学生是人,同时亦是学生,一个人可以有多重身份
Person p1 = new Student();
// Person父类型,可以指向子类,但不能调用子类独有的方法
Object o1 = new Student();
// 父类的引用指向子类的类型,能执行什么方法其实取决于等号左边的引用类型
s1.run(); // 当子类重写了父类的方法,执行的是子类的方法
p1.run();
s1.eat();
p1.eat(); // 当引用的父类没有相关方法时,无法执行
}
}
package Demo;
// 一个项目应该只存在一个main方法
public class Application {
public static void main(String[] args) {
// 一个对象的实际类型是确定的,但可以指向的引用类型不确定
Student s1 = new Student();
// 下面被称为父类的引用指向子类的类型,实际类型是student,引用的是person
// 可以直观理解为,学生是人,同时亦是学生,一个人可以有多重身份
Person p1 = new Student();
Object o1 = new Student();
// 父类的引用指向子类的类型,能执行什么方法其实取决于等号左边的引用类型
s1.run(); // 当子类重写了父类的方法,执行的是子类的方法
p1.run();
((Student) p1).eat(); // 将类型强制转换为Student后可以调用
}
}
-
多态注意事项
-
多态是方法的多态,属性没有多态
-
父类和子类,类之间必须有联系,错误的会报类型转换异常
-
存在条件:类之间存在继承关系,方法需要被重写,同时父类引用指向子类对象
Father f = new Son()
-
有几种情况无法被重写
-
static
方法,静态方法属于类,不属于实例 -
final
定义的是常量 -
private
私有方法
-
-
instanceof和类型转换
-
instanceof
判断类型是否一致 -
多态:父类引用指向子类的对象
-
把子类转换为父类,叫做向上转型,不用强制转换
-
把父类转换为子类,称作向下转换,需要强转换
-
作用在于调用的时候把对象升级或者降级一下就可以调用,方便方法的调用,减少重复代码
package Demo;
// 在java中,所有类都直接或者间接继承`Object`类
public class Person {
public void run() {
System.out.println("run");
}
}
package Demo;
public class Teacher extends Person {
}
package Demo;
// 子类继承父类,就会拥有父类的属性和全部方法
// 注意,如果父类使用了private修饰符,则子类就无法继承,一般属性都是私有的,公共的都是方法
public class Student extends Person {
@Override
public void run() {
System.out.println("son");
}
public void eat() {
System.out.println("eat");
}
}
package Demo;
// 一个项目应该只存在一个main方法
public class Application {
public static void main(String[] args) {
// 一个对象的实际类型是确定的,但可以指向的引用类型不确定
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);
// System.out.println(object instanceof String); 编译报错,同级指标不能互相转换
// 多态的操作实际上某种意义上进行了类型的轻质转换,从低类型转换为了高类型
// 子类转换为父类可能会丢失一些方法
Person p1 = new Student();
((Student)p1).eat();
}
}
static关键字详解
-
静态变量:用
static
修饰的变量,此类变量支持对象调用,且支持通过类名调用,也被称作类变量.当一个变量需要被很多类调用的时候可以使用 - 静态方法与静态变量基本一致
package Demo;
// 一个项目应该只存在一个main方法
public class Application {
private static int age;
private double score;
public static void main(String[] args) {
Application application = new Application();
System.out.println(Application.age); // 通过类的方式调用
System.out.println(application.age); //通过对象调用
go(); //静态对象与类一起加载,直接调用
}
public static void go() {
System.out.println("go");
}
}
-
在
java
的类主体代码当中可以用{}
来表示匿名代码块package Demo; // 静态导入包 import static java.lang.Math; // 一个项目应该只存在一个main方法 public class Application { // 匿名代码块意味着可以用来进行一些初始赋值,初始化 { System.out.println("匿名代码块"); } // 静态代码块只执行一次,只有第一次执行 static { System.out.println("静态代码块"); } public Application() { System.out.println("构造函数"); } public static void main(String[] args) { Application application1 = new Application(); System.out.println("============"); Application application2 = new Application(); System.out.println(Math.random()); } }
-
通过
final
修饰的类不能被继承
抽象类
-
abstract
修饰符可以用来修饰方法也可以修饰类,修饰方法,该方法就是抽象方法;修饰类,就是休市内 - 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类
-
抽象类,不能使用
new
关键字来创建对象,主要是用来让子类继承的 - 抽象方法,只有方法的声明而没有方法的实现,是用来让子类实现的
- 子类继承抽象类,必须要实现抽象类没有实现的方法,否则该子类也要声明为抽象类
package Demo;
// abstract用于标识抽象类,该抽象类是寄希望于有人根据自己需要进行实现,抽象类主要起约束作用
public abstract class Person {
// 抽象方法(只有方法名),具体功能别人实现,这里主要起到约束作用
public abstract void doSomething();
}
package Demo;
// 抽象类的所有方法,继承了它的子类,都必须要实现相应方法,除非这个子类也是抽象类
// java类只允许继承一个类,即单继承
public class Teacher extends Person {
@Override
public void doSomething() {
System.out.println("do it now");
}
}
接口定义与实现
- 普通类:只有具体实现
- 抽象类:具体实现和规范(抽象方法)都有
- 接口:只有规范,自己无法写方法,实现了约束和实现分离.工作中往往是面向接口编程,把接口定义好了,然后下面的人去实现就行
- 接口就是规范.定义的是一组规则,就是说如果你是某一种东西,则必须能做到对应的事,体现了现实世界中“如果你是…则必须能…”的思想.例如:如果你是人,则必须能做到人能做到的事(思考等)
- 接口的本质是契约,就跟法律一样,制定好后大家都遵守
- 面向对象的精髓,是对对象的抽象.最能体现这一点的就是接口(因为都是抽象的规范,没有具体实现).为什么讨论设计模式都只针对具有抽象能力的语言,因为设计模式研究的,就是如何合理地去抽象.
-
声明类的关键词
class
,声明接口的关键词interface
package Demo;
// 接口都需要有实现类
public interface UserService {
// 接口的所有定义都默认是抽象的,public abstract这个可以默认不写
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
package Demo;
public interface TimeService {
void timer();
}
package Demo;
// 实现类的命名规范 类+实现类名称+implements+接口名称
// 实现了接口的类,就需要重写接口的方法,利用接口实现多继承(实际是实现)
public class UserInterfaceImpl implements UserService,TimeService{
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
@Override
public void timer() {
}
}
-
总结
- 接口主要提供规范和约束,不提供具体实现
- 接口通过定义规范让不同的人去完成具体的规范,实现大规模协作
-
接口中方法默认是
public abstract
-
接口中属性默认是
public static final
- 接口不能被实例化,接口没有构造方法
-
implements
可以实现多个接口,变相实现了多继承 - 接口的实现必须要重写接口中的方法
内部类
-
内部类就是在一个类的内部定义一个类.比如,A类中定义一个B类,那么B类相对A类来说就被称为内部类,而A类相对B类来说就是外部类
-
以下几种类型
-
成员内部类
package Demo; public class Outer { private int id = 10; public void out() { System.out.println("这是外部类的方法"); } public class Inner { public void in() { System.out.println("这是内部类的方法"); } // 内部类可以获得外部类的私有属性 public void getID() { System.out.println(id); } } }
package Demo; // 一个项目应该只存在一个main方法 public class Application { public static void main(String[] args) { Outer outer = new Outer(); outer.out(); // 通过外部类实例化内部类 Outer.Inner inner = outer.new Inner(); inner.in(); inner.getID();; } }
-
静态内部类
package Demo; public class Outer { private int id = 10; public void out() { System.out.println("这是外部类的方法"); } // 静态内部类,此时无法获取非静态的属性 public static class Inner { public void in() { System.out.println("这是内部类的方法"); } } }
-
局部内部类
package Demo; // 一个java文件中可以有多个class类,但只能有一个public class public class Outer { private int id = 10; public void method() { // 局部内部类 class Inner { public void in() { } } } }
-
匿名内部类
package Demo; // 一个java文件中可以有多个class类,但只能有一个public class public class Outer { private int id = 10; public void method() { // 局部内部类 class Inner { public void in() { } } } }
package Demo; // 一个项目应该只存在一个main方法 public class Application { public static void main(String[] args) { // 没有名字初始化类,匿名内部类,不用将实例保存到对象中 // 接口也一样 new Outer().method(); } }
-