面向对象(OPP)
强调:所有的知识点都是结合代码理解,代码记录了学习的详细笔记,需要结合代码去深刻理解所需要学习的知识点!!!
1. 面向过程 & 面向对象
- 面向过程思想
- 步骤清晰简单,第一步做什么,第二步做什么……
- 面对过程适合处理一些较为简单的问题
- 面向对象思想
- 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
- 面向对象适合处理复杂的问题,适合处理需要多人协作的问题!
- 对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
2. 什么是面向对象
本质
- 以类的方式组织代码,以对象的方式组织(封装)数据。
三大特性
-
封装
-
继承
-
多态
3. 方法的定义(回顾)
- 修饰符
- 返回值类型
- 方法名
- 注意规范:首字母小写、驼峰原则
- 见名知意
- 参数列表:(参数类型 参数名,…)
- 异常抛出
- break 和 return 的区别
- break:跳出Switch,结束循环
- return:结束方法,返回一个结果,可以为空
4. 方法的调用(回顾)
-
静态方法
public static void a(){}
-
非静态方法
public void a(){}
-
形式参数和实际参数
-
值传递和引用传递
-
this关键字
代码
import java.io.IOException;
//Demo01 类
public class Demo01 {
//Main 方法
public static void main(String[] args) {
}
/*
修饰符 返回值类型 方法名(....){
//方法体
return 返回值;
}
*/
//return 结束方法,返回一个结果 可以为空
public String sayHello(){
return "hello,world";
}
//int a ,int b 形式参数
public int max(int a,int b){
return a>b ? a : b; //三元运算符
}
//数组下标越界:Arrayindexoutofbounds
// 抛出异常
public void readFile(String file) throws IOException{
}
}
public class Demo02 {
//静态方法 static
//非静态方法
public static void main(String[] args) {
//使用Student类中的非静态方法
//需要实例化这个类 new
//对象类型 对象名 = 对象值
Student student = new Student();
student.say();
}
// 和类一起加载的 先
public static void a(){
}
//类实例化之后才存在 后
public void b(){
}
}
//学生类
public class Student {
//非静态方法
public void say(){
System.out.println("学生说话了");
}
}
public class Demo03 {
public static void main(String[] args) {
//实际参数和形式参数的类型要对应!
int add = Demo03.add(1,2);
System.out.println(add);
}
public static int add(int a,int b){
return a+b;
}
}
//值传递
public class Demo04 {
public static void main(String[] args) {
int a = 1;
System.out.println(a);
Demo04.change(a);
System.out.println(a); //1
}
// 返回值为空
public static void change(int a){
a = 10;
}
}
//引用传递: 对象,本质还是值传递
public class Demo05 {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name); //null
Demo05.change(person);
System.out.println(person.name);//王
}
public static void change(Person person){
// person是一个对象 指向 --->Person person = new Person();这是一个具体的人,可以改变属性
person.name = "王";
}
}
//定义了一个Person类 有一个属性:name
class Person{
String name;
}
5.类与对象的关系
- 类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物。
- 动物、植物、手机、电脑…
- Person类、Pet类、Car类等,这些类都是用来描述/定义某一类具体的事物应该具备的特点和行为
- 对象是抽象概念的具体实例
- 张三就是人的一个具体实例
- 能够体现出特点,展现出功能的是具体的实例,而不是一个抽象的概念
6.类与对象的创建
创建与初始化对象
-
使用new关键字创建对象
-
使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
-
类中的构造器也称为构造方法,是在进行创建对象时必须要调用的,并且构造器有一下俩个特点:
- 必须和类的名字相同
- 必须没有返回类型,也不能写void
-
构造器必须要掌握!!!
代码
//一个项目应该只存在一个main方法
public class Application {
}
//学生类
public class Student {
//属性 : 字段
String name; // null
int age; // 0
//方法
public void study(){
System.out.println(this.name + "在学习");
}
}
/*
public static void main(String[] args) {
//类:抽象的,需要实例化
//类实例化后会返回一个自己的对象
//student对象就是一个Student类的具体实例!
Student student = new Student();
Student xiaowang = new Student();
xiaowang.name = "小王";
xiaowang.age = 18;
System.out.println(xiaowang.name);
System.out.println(xiaowang.age);
System.out.println(student.name);
System.out.println(student.age);
}
*/
//java文件 --编译--> class文件
public class Person {
//一个类即使什么都不写,它也会存在一个方法
//显示的定义构造器
String name;
int age;
//实例化初始值
//1.使用new关键字,必须要有构造器 本质是在调用构造器
//2.用来初始化值
public Person(){ //无参构造 默认的构造器
}
//有参构造 一旦定义了有参构造,无参构造就必须显式定义
public Person(String name){//重载
this.name = name;
}
//alt + insert 生成构造器
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
/*
public static void main(String[] args) {
//new 实例化了一个对象
Person person = new Person("hahah");
System.out.println(person.name);
}
构造器:
1.和类名相同
2.没有返回值
作用:
1.new 本质在调用方法
2.初始化对象的值
注意点:
1.定义有参构造之后,如果向使用无参构造,现实的定义一个无参的构造
Alt + Insert --> Constructor
this. =
*/
创建对象内存分析
代码
public class Application {
public static void main(String[] args) {
Pet dog = new Pet();
dog.name = "旺财";
dog.age = 21;
dog.shout();
System.out.println(dog.name);
System.out.println(dog.age);
Pet cat = new Pet();
}
}
public class Pet {
//属性 成员变量
/*
默认初始化
数字:0 0.0
char:u0000
boolean:false
引用类型:null
*/
//属性的定义 修饰符 属性类型 属性名 = 属性值
public String name;
public int age;
//有无参构造
public void shout(){
System.out.println("叫了一声");
}
}
7. 封装
- 该露的露,该藏的藏
- 我们程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。
- 封装(数据的隐藏)
- 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
- 记住这句话就够了: 属性私有 get/set
代码
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("王");
System.out.println(s1.getName());
s1.setAge(999);//不合法的
System.out.println(s1.getAge());
}
}
//类 private 私有
public class Student {
//属性 :名字 学号 性别
private String name;
private int id;
private char sex;
private int age;
//提供一些可以操作这个属性的方法!
//提供一些public的get、set方法
//get 获得这个数据
public String getName() {
return this.name;
}
//set 给这个属性设置值
public void setName(String name){
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age>120||age<0){//安全性验证判断 合理
this.age = 3;
}else{
this.age = age;
}
}
//Alt + Insert --> Getter and Setter
}
意义
- 提高程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口
- 系统的可维护性增加了
继承
- 继承是类和类之间的一种关系:子类(派生类)、父类(基类),除此之外,类和类之间的关系还有依赖、组合、聚合等。
- extends:“扩展”,子类是父类的扩展,子类和父类间从意义上讲应该具有" is a "的关系
- JAVA中类只有单继承,没有多继承!:一个爸爸可以有很多儿子,一个儿子只有一个爸
Object类
在Java中,所有的类都默认直接或间接继承object类
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.say();
System.out.println(student.getMoney());
}
}
//Person 人 :基类,父类
//在Java中,所有的类都默认直接或间接继承object类
public class Person {
//public 公共的
//protected 受保护的
//default 默认的,
//private 私有的
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;
}
// Student is 人 :派生类,子类
//子类继承父类,就会拥有父类的全部方法
public class Student extends Person{
//Ctrl + H
}
//Teacher is 人 :派生类,子类
public class Teacher extends Person{
}
super类★
-
super是调用父辈的构造方法,必须在构造方法的第一个
-
super必须只能出现在子类的方法或者构造方法中!
-
super 和 this 不能同时调用构造方法!
-
Vs this
- 代表的对象不同
- this :本身调用者这个对象
- super:代表父类对象的应用
- 前提
- this:没有继承也可以使用
- super:只能在继承条件才可以使用
- 构造方法
- this(); 本类的构造
- super(); 父类的构造
- 代表的对象不同
public class Application {
public static void main(String[] args) {
Student student = new Student();
//student.test("王中王");
student.test1();
}
}
public class Person {
public Person() {
System.out.println("Person无参执行了");
}
protected String name = "王王";
//private 私有的东西无法被继承
public void print(){
System.out.println("Person");
}
}
public class Student extends Person{
public Student() {
//隐藏代码:调用了父类的无参构造
//super();调用父类的构造器,必须要在子类构造器的第一行
System.out.println("Student无参执行了");
}
private String name = "王";
public void print(){
System.out.println("Student");
}
public void test1(){
print(); //Student
this.print();//Student
super.print();//Person
}
public void test(String name){
System.out.println(name); //王中王
System.out.println(this.name);//王
System.out.println(super.name);//王王
}
}
方法重写★
- 首先区别静态方法
public class Application {
public static void main(String[] args) {
//方法的调用只跟左边:定义的数据类型有关
A a = new A();
a.test();//A=>test()
//父类的引用指向了子类
B b = new A();
b.test();//B=>test()
}
}
//重写都是方法的重写,和属性无关
public class B {
public static void test(){
System.out.println("B=>test()");
}
}
//A是子类 A继承了B
public class A extends B{
public static void test(){
System.out.println("A=>test()");
}
}
- 非静态方法
public class Application {
public static void main(String[] args) {
//方法的调用只跟左边:定义的数据类型有关
A a = new A();
a.test();//A=>test()
//父类的引用指向了子类
B b = new A();
b.test();//A=>test() 父类方法被重写
}
}
//重写都是方法的重写,和属性无关
public class B {
public void test(){
System.out.println("B=>test()");
}
}
//A是子类 A继承了B
public class A extends B{
//Override 重写 Alt + Insert --> Override
@Override //注解:有功能的注释
public void test() {
System.out.println("A=>test()");
}
}
-
总结
-
静态与非静态方法的区别很大
-
需要有继承关系,子类重写父类的方法
-
Alt + Insert --> Override
-
方法名必须相同
-
参数列表必须相同
-
修饰符的范围可以扩大,不可以缩小
- ↓public
- Default
- Protected
- ↑private
-
抛出的异常范围可以被缩小,不可以扩大
- 例如Exception --> ClassNotFoundException
-
-
所以重写子类的方法和父类的方法必须要一致,方法体不同
-
为什么需要重写
- 父类的功能,子类不一定需要或不一定满足
-
不能被重写的方法
- static方法:属于类,不属于实例
- final 常量
- private 私有方法
-
多态
-
即同一个方法可以根据发送对象的不同而采用多种不同的行为方式
-
一个对象的实际类型是确定的,但可以指向对象的引用类型有很多(父类,有关系的类)
-
存在条件
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类对象
-
注意事项
- 多态是方法的多态,与属性无关
- 父类子类要有联系
- 类型转换异常 -->ClassCastException
public class Application {
public static void main(String[] args) {
//Student 子类 能调用的方法都是自己的或者继承父类的
Student s1 = new Student();
//Person 父类 可以指向子类,但是不能调用子类独有的方法
Person s2 = new Student();
Object s3 = new Student();
//对象能执行哪些方法,只要看对象左边的类型,和右边关系不大
s2.run();//子类重写了父类的方法,执行子类的方法
s1.run();
((Student)s2).eat();//强制转换
}
}
public class Person {
public void run(){
System.out.println("run");
}
}
public class Student extends Person{
@Override
public void run() {
System.out.println("son");
}
public void eat(){
System.out.println("eat");
}
}
instanceof & 类型转换
- Person类
public class Person {
public void run(){
System.out.println("run");
}
}
- Student类
public class Student extends Person{
public void go(){
System.out.println("go");
}
}
- Teacher类
public class Teacher extends Person{
}
- instanceof
- 万能公式:System.out.println(x instanceof y);
- 判断两个类是否有关系
public class Application {
print static void main(String args[]){
// Object > String
// Object > Person > Student
// Object > Person > Teacher
Object object = new Student();
//System.out.println(x instanceof y);//能不能编译通过,判断是否有关系
System.out.println(object instanceof Student);//ture
System.out.println(object instanceof Person);//ture
System.out.println(object instanceof Object);//ture
System.out.println(object instanceof Teacher);//false
System.out.println(object instanceof String);//false
Person person = new Student();
System.out.println(person instanceof Student);//ture
System.out.println(person instanceof Person);//ture
System.out.println(person instanceof Object);//ture
System.out.println(person instanceof Teacher);//false
//System.out.println(person instanceof String);//编译报错 person和String没有半毛钱关系
}
}
- 类型转换
- 把子类转换为父类,向上转换
- 把父类转换成子类,向下转换,强制转换
- 可能丢失一些方法
- 方便方法的调用,减少重复的代码
public class Application {
public static void main(String[] args) {
//类型之间的转换:父 子
//高------------------低
Person person = new Student();
//student将这个对象转换成Student类型,我们就可以使用Student类型的方法了
/*Student person1 = (Student)person;
person1.go();*/
((Student)person).go();
//子类转换为父类,可能丢失自己的本来一些方法
Student student = new Student();
student.go();
}
}
Static:静态
- 非静态方法可以调用静态方法,静态方法无法调用非静态方法
//Static :静态
public class Student {
//静态变量
private static int age;
//非静态变量
private double score;
public void run(){}
public static void go(){}
public static void main(String[] args) {
//j
new Student().run();
go();
Student s1 = new Student();
System.out.println(Student.age);
//System.out.println(Student.score); 报错
System.out.println(s1.score);
System.out.println(s1.age);
}
}
public class Person {
{
System.out.println("匿名代码块");//2.赋初始值
}
static {
System.out.println("静态代码块");//1.只执行一次
}
public Person() {
System.out.println("构造方法");//3.
}
public static void main(String[] args) {
Person person = new Person();
Person person1 = new Person();
}
}
//静态导入包
import static java.lang.Math.random;
public class Test {
public static void main(String[] args) {
//random()返回一个随机数 静态导入包使Math.可省略
System.out.println(Math.random());
}
}
抽象类
- abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类。
- 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。
- 抽象类,不能使用new关键字来创建对象,它是用来让子类继承的。
- 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
- 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。
//abstract 抽象类 类 extends:单继承 --> 接口多继承
public abstract class Action {
//约束 有人帮我们实现
//抽象方法,只有方法名字,没有方法的实现
public abstract void doSomething();
/*
特点
1.不能new这个抽象类,只能靠子类去实现它:约束
2.抽象类中可以写的方法
3.抽象方法必须在抽象类中
*/
}
//抽象类的所有方法,继承了它的子类,都必须要实现他的方法,除非子类也是抽象类
public class A extends Action {
@Override
public void doSomething() {
}
}
- 总结
- 有抽象方法的类必然是抽象类
- 抽象类不可以被实例化,不能被new来实例化抽象类
- 抽象类可以包含属性,方法,构造方法,但是构造方法不能用来new实例,只能被子类调用
- 抽象类只能用来继承
- 抽象类的抽象方法必须被子类继承
- 意义
- 为子类提供一个公共的类型
- 封装子类中重复内容(成员变量和方法)
- 定义有抽象方法,子类虽然有不同的实现,但该方法的定义是一致的
接口
- interface :接口
//抽象的思维!!!
//interface 定义的关键字 接口都需要有实现类
public interface UserService {
//接口中的所有定义的方法其实都是抽象的 public abstract
//静态常量 public static final
int AGE = 99;
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
public interface TimeService {
void timer();
}
//一个类可以实现接口 implements 接口
//实现了接口的类,就需要重写接口的方法
//多继承 利用接口实现多继承
public class UserServiceImpl 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 abstract)
- 接口的变量会被隐式的指定为public static final 变量(只能是public,用private修饰会报编译错误)
- 接口不能被实例化,且接口中没有构造方法
- 使用implements可以实现多个接口,在类声明中,Implements关键字放在class声明后面
- 必须要重写接口中的方法
抽象类和接口的区别
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
内部类
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.in();
inner.getID();
}
}
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);
}
}
}
//一个java类中可以有多个class类,但是只能有一个public class
public class Outer2 {
//局部内部类
public void method(){
class Inner{
public void in(){
}
}
}
}
public class Test {
public static void main(String[] args) {
//没有名字初始化类,不用将实例保存到变量中
new Apple().eat();
new UserService() {
@Override
public void hello() {
}
};
}
}
class Apple{
public void eat(){
System.out.println("1");
}
}
interface UserService{
void hello();
}