面向过程&面向对象
面向过程思想:
- 步骤清晰简单,第一步该做什么,第二步该做什么...
- 适合处理一些较为简单的问题
线性思维
面向对象四思想:
- 物以类聚,分类的思维模式,思考问题首要解决问题需要哪些分类,然后对这些分类进行单独思考,最后才对某个分类下的细节进行面向过程的探索
- 面向对象适合处理复杂的问题,适合处理多人协作问题
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统,但是具体到细微的步骤仍需面向过程的思路去解决
什么是面向对象
面向对象编程:Object-Oriented Programming,OOP
面向对象编程的本质:以类的方式组织代码,以对象的组织(封装数据)
抽象
三大特性:
-
封装
-
继承
-
多态
从认识论的角度考虑是先有对象后有类,对象,是具体的事物。类,是抽象的,是对对象的抽象
从代码运行的角度考虑,是先有类后有对象,类是对象的模板
回顾方法及加深
方法的定义
- 修饰符
- 返回值类型
- break:跳出switch,结束循环 和return的区别
- 方法名(注意规范,驼峰命名 得见名知意)
- 参数列表 (参数名,参数类型)(可变参数:参数类型+...)
- 异常抛出
方法的调用:递归
- 静态方法
- 非静态的方法
- 形参和实参
- 值传递和引用传递
- this关键字
方法回顾
1.什么是方法
package OOP;//Demo01就是一个类
public class Demo01 {//main方法public static void main(String[] args) {}/*修饰符 返回值类型 方法名(参数){方法体return 返回值;}*///return 结束方法,返回一个结果public static String print(){return "hello";}public void a(){return;}public int max(int a,int b){return a > b ? a : b; //三元运算符}
}
2.方法的调用
首先创建一个学生类
package OOP;//学生类
public class Student {//静态方法public static void say(){System.out.println("学生说话了");}public void Say(){System.out.println("学生没有说话");}
}
然后去调用学生类
public class Demo02 {public static void main(String[] args) {//静态方法调用Student.say();//非静态方法调用//实例化这个类 new//对象类型 对象名 = 对象值;Student student = new Student();student.Say();}//同一个类中,两个方法可以互相调用,但是当一个为静态方法,有static,一个为非静态方法,则不行//若加了static,它和类一起加载的public void a(){b();}//类实例化之后才存在public void b(){a();}
}
3.实参和形参
public class Demo03 {public static void main(String[] args) {//实际参数和形式参数的类型要一一对应//当下面方法为非静态时这样输出Demo03 a = new Demo03();int b = a.add(1,2);System.out.println(b);//当方法为静态时这样调用
// int a = Demo03.add(1,2);
// System.out.println(a);}//注意该位置有无staticpublic static int add(int a, int b){return a+b;}
}
值传递
package OOP;//值传递
public class Demo04 {public static void main(String[] args) {int a = 1;System.out.println(a); //输出1Demo04.change(a);System.out.println(a); //输出1,输出的依旧是上面那个a,change方法返回值为空}//返回值为空,将a=1传给该方法,但是走完之后未将该方法的值再传递回去public static void change(int a){a = 10;}
}
引用传递
package OOP;//引用传递:对象,本质还是值传递
//对象,内存
public class Demo05 {public static void main(String[] args) {Person i = new Person();System.out.println(i.name); //未对其定义,输出nullDemo05.change(i);System.out.println(i.name); //未对其定义,输出蔡徐坤}public static void change(Person i){//i是一个对象:指向的是--->Person i = new Person();这是一个具体的对象,就可以改变它的属性i.name = "蔡徐坤";}
}//定义了一个person类,有一个属性:name
class Person{String name;
}
类与对象的关系
类是一种抽象的数据类型,它是对某一事物整体描述/定义,但是并不能代表某一具体事物
- 动物,手机,电脑
- Person类,Pet类,Car类等,这些都是用来描述/定义某一事物应该具备的特点和行为
对象是抽象概念的具体实例
- 张三这个人就是人的具体实例,他家的狗就是狗的具体实例
- 能够体现出特点,展现出功能的是具体实例,而不是一个抽象的概念
创建与初始化对象
使用new关键字创建对象
在使用new关键字创建的时候,除了分配内存空间外,还会给创造好的对象进行默认初始化及对类中构造器的调用
类中的构造器也称构造方法,是在创建对象时必须要调用的,构造器有以下俩特点:
- 必须和类的名字相同
- 必须没有返回类型,也不能写void
构造器必须掌握
先创建一个学生类
package OOP.demo02;//学生类
public class Student {//属性:字段String name; //nullint age; //0//方法public void study(){System.out.println(this.name+"在学习");}
}
再创建一个运行方法
package OOP.demo02;//一个项目应该只存在一个main方法
public class Application {public static void main(String[] args) {//类:抽象的,实例化//类实例化后会返回一个自己的对象//该地方student对象就是一个Student类的具体实例Student xiaoming = new Student();Student dongfeng = new Student();xiaoming.name = "小明";xiaoming.age = 18;System.out.println(xiaoming.name);System.out.println(xiaoming.age);dongfeng.name = "东风";dongfeng.age = 18;System.out.println(dongfeng.name);System.out.println(dongfeng.age);}
}
ps:在类中,应只有属性和方法,启动的main方法应单独在一个类中
构造器详解
package OOP.demo02;//java----->生成class文件
public class Person {//一个类即使什么都不写,也会存在一个方法,默认构造器//显示的定义构造器String name;//无参构造//1.使用new关键字必须要有构造器,本质是在调用构造器//2.实例化初始值public Person(){this.name = "蔡徐坤"; //一旦写了下面的有参构造,这个构造器必须写出来且为空}//有参构造//一旦定义了有参构造,无参就必须显示定义public Person(String name){this.name = name;}
}
/*
public static void main(String[] args) {//new 实例化一个对象Person person = new Person("蔡徐坤"); //倘若为无参构造可空,有参得给值System.out.println(person.name);}构造器:1.和类名相同2.无返回值作用:1.new 本质在调用构造器2.初始化对象的值注意点:1.定义有参构造后,如果想使用无参构造,显示的定义了一个无参构造,也就是空alt + insert 快捷插入构造器this. = ;this. 代表当前的值;= 后是传进来的值*/
简单小结与对象
/*1.类与对象类是一个模板,抽象的;对象则是一个具体的实例2.方法定义、调用3.对象的引用引用类型: 基本类型(8个)对象是通过引用来操作的:栈 ----> 堆4.属性:字段filed 成员变量默认初始化 数字则是0,char则是:u0000 ,boolean:false ,引用:null修饰符 属性类型 属性名 = 属性值5.对象的创建和使用必须使用new关键字去创建对象,构造器 Person person = new Person对象的属性 caiXuKun.name对象的方法 caiXuKun.sleep();6.类:静态的属性 属性动态的行为 方法7.三大特征:封装继承多态
*/
封装
该露的露,该藏得藏
程序设计追求:高内聚,低耦合
高内聚:类内部的数据操作细节自己完成,不允许外部干涉
低耦合:仅暴露少量方法给外部使用
封装:数据的隐藏
通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏
记住一句话:属性私有,get/set
有时set里面会加一个安全性验证
举个栗子
先创建一个学生类
package OOP.demo04;//学生类 private:私有
public class Student {//下面将public换成private就是将属性私有//名字private String name;
// public String name;//学号private int id;//年级 grade:成绩,等级,年级private int grade;//性别private char sex;//年龄private int age;//提供一些可以操作这个属性的方法//提供一些publilc的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 >= 150 || age <= 0) {this.age = 0;}else {this.age = age;}}//这是方法//学习()//睡觉()/*alt + insert 也可快捷设置get和set方法*/
}
然后创建一个测试类
package OOP.demo04;public class Application {public static void main(String[] args) {Student s1 = new Student();
// s1.name = "蔡徐坤"; 将private换成pubilc才能这样读取//被封装后,以下面方式开始读取String name = s1.getName();//赋值s1.setName("牢大");System.out.println(s1.getName());s1.setAge(999); //该输入不合法System.out.println(s1.getAge());}
}
封装的意义:
- 提高程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口,形成规范
- 提高系统的可维护性
继承
本质:是对某一批类的抽象,从而实现对现实世界更好的建模
extends的意思是“扩展”,子类是父类的扩展
java中只有单继承,没有多继承
一个儿子只有一个父亲,但是一个父亲可以有多个儿子
- 继承是类与类的一种关系,除此以外,类与类之间的关系还有依赖,组合,聚合
- 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,用关键字extends表示
- 子类与父类之间从意义上讲,应该具有‘is a’的关系
object类
super
方法重写
让我们举个栗子
先定义一个父类
package OOP.demo05;//在java中所有的类都默认直接或间接继承Object类
//Person 人;父类、基类
public class Person {//public 公共的,子类可以调用//protected 受保护的,优先级比default高一点//private 私有的,子类无法调用//default 默认的private int money = 10_0000_0000;//删去private为默认,可以加该关键词,也可以不加,需要其他关键词换掉private即可public int getMoney() {return money;}public void setMoney(int money) {this.money = money;}
}
再给出他的两个子类
package OOP.demo05;//Student is 人;派生类,子类
//子类继承了父类,就会拥有父类的全部方法
public class Student extends Person {//crtl + h 指出哪个类继承哪个类,给出关系示例图
}
package OOP.demo05;//老师 is 人;派生类,子类
public class Teacher extends Person {
}
最后给出测试类
package OOP.demo05;public class Application {public static void main(String[] args) {Student s = new Student();System.out.println(s.getMoney());}
}
可以看出,student类中并没有任何属性,但是研究可以调用money这个属性,他继承了person这个类的所有属性,所以可以调用
super详解
直接上代码展示
先写父类
package OOP.demo06;public class Person {public Person() {System.out.println("Person执行无参构造");}protected String name = "蔡徐坤";//假若这里修饰词是priva,即使用super也无法调用public void print(){System.out.println("鸡你实在太美");}
}
再写子类
package OOP.demo06;public class Student extends Person {public Student() {//隐藏代码:调用了父类的无参构造 ----> super();super(); //调用父类的构造器,必须在子类构造器的第一行System.out.println("student执行无参构造");}//在这里有个问题,假如父类没有无参构造,那么子类也无法进行无参构造private String name = "基尼太美";public void test1(String name) {System.out.println(name); //这是我传给他的参数System.out.println(this.name); //是类里面的参数System.out.println(super.name); //用来访问父类里的参数}public void print(){System.out.println("good good study");}public void test2(){print(); //当前类的方法this.print(); //当前类的方法super.print(); //父类里面的方法}
}
再写测试类
package OOP.demo06;public class Application {public static void main(String[] args) {Student student = new Student();System.out.println("==============");/*会输出:Person执行无参构造student执行无参构造这里未对父类进行实例化,但是依旧输出了父类证明先对父类进行无参构造,再对子类进行无参构造*/student.test1("kunkun");System.out.println("===============");student.test2 ();}
}/*
最终输出结果一览Person执行无参构造
student执行无参构造
==============
kunkun
基尼太美
蔡徐坤
===============
good good study
good good study
鸡你实在太美*/
super注意点:
- super调用父类的构造方法,必须在构造方法的第一个
- super必须只能出现在子类的方法或者构造方法中
- super和this不能同时调用构造方法
VS this
- 代表的对象不同: this ---> 本身调用者这个对象; super ----> 代表父类对象的应用
- 前提:this ---> 没有继承也可以使用;super ----> 只能在继承条件下才能使用
- 构造方法:this(); 默认调用本类的构造 super(); 父类的构造
方法重写
让我们举个栗子
先写一个父类,A类
package OOP.demo07;//重写都是方法的重写,与属性无关
public class A {public void test(){System.out.println("A ----> test");}
}
再写一个子类,B类
package OOP.demo07;//继承
public class B extends A {//override 重写@Override //注解,有功能的注释public void test() {
// super.test(); //默认调用父类的方法,也可重写自己的方法System.out.println("B ----> test");}
}
最后写一个测试类
ackage OOP.demo07;public class Application {public static void main(String[] args) {//静态方法和非静态方法区别很大//在静态方法中:方法的调用只和左边定义的数据类型有关//非静态 -----> 重写//在重写的方法中,修饰词也只能为public,如果为private,则无法重写//重写的方法方法名和参数都一样,但是只能子类重写父类的方法B b = new B();b.test(); //走b类调用方法//父类的引用指向了子类A a = new B();//在非静态方法中,这叫子类重写了父类的方法a.test(); //走a类调用方法}/*在A,B类中,方法中加了static静态,输出为B ----> testA ----> test将static删除,输出B ----> testB ----> test*/
}
重写:
- 需要有继承关系,子类重写父类方法
- 1.方法名必须相同
- 2.参数列表必须相同
- 3.修饰符:范围可以扩大,但是不能缩小: public > protected > default > private
- 4.抛出的异常:范围可以被缩小,但是不能扩大
重写:子类的方法必须和父类方法一致,方法体不同
为什么需要重写?
1.父类的功能,子类不一定需要或者不被满足
快捷键:alt + insert ,选中 overide,也就是重写
2.当重写时,idea在代码行数边会出现小图标
多态
可以实现动态编译,增强可扩展性
什么是多态:
- 即同一方法可以根据发送对象的不同而采取多种不同行为方式
- 一个对象的实际类型是确定的,但是可以指向对象的引用的类型有很多(父类或有关系的类)
多态存在的条件
- 有继承方法
- 子类重写父类方法
- 父类引用指向子类对象
注意
多态是方法的多态,属性没有多态性
instanceof
类型转换 --> 引用类型转换
让我们来举个栗子
先创建一个Person类
public class Person {public void run(){System.out.println("Student run");}
}
再创建一个学生类
public class Student extends Person {@Overridepublic void run() {System.out.println("maike run");}public void eat(){System.out.println("eat");}
}
/*多态注意事项:1.多态是方法的多态,属性没有多态2.父类和子类,有联系,不行会报:类型转换异常 ClassCastException!3.存在条件: 继承关系,方法需要重写,父类引用指向子类对象 Father f1 = new Son();有些方法不能被重写:static 方法,属于类,他不属于实例final 常量private 该修饰的方法被私有,无法重写*/
最后的惯例测试类
public class Application {public static void main(String[] args) {//一个对象的实际类型是确定的
// new Student();
// new Person();//可以指向的引用类型不确定:父类的引用指向子类//Student 能调用的方法都是自己的或者继承父类的!Student s1 = new Student();//Person 父类的,可以指向子类,但不能调用子类独有的方法Person s2 = new Student();Object s3 = new Student();//对象能执行哪些方法,主要看对象左边的类型和右边关系不大s2.run(); //子类重写了父类的方法,执行子类的方法
// s2.eat(); //父类无方法,所以在这里他就不能这样去调用子类中的eat方法s1.run();((Student) s2).eat(); //这样写才能从父类调用子类独有的方法//该写法是强制类型转换,将s2这个高类型转换为了Student这个低类型}
}
instanceof 和 类型转换
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 {
}
最后来个测试类
public class Application {public static void main(String[] args) {//Object > String//Object > Person > Teacher//Object > Person > Student// x instanceof y 能不能编译通过 接口Object s1 = new Student();System.out.println(s1 instanceof Student); //trueSystem.out.println(s1 instanceof Person); //trueSystem.out.println(s1 instanceof Object); //trueSystem.out.println(s1 instanceof Teacher); //falseSystem.out.println(s1 instanceof String); //falseSystem.out.println("============");Person s2 = new Student();System.out.println(s2 instanceof Student); //trueSystem.out.println(s2 instanceof Person); //trueSystem.out.println(s2 instanceof Object); //trueSystem.out.println(s2 instanceof Teacher); //false
// System.out.println(s2 instanceof String); //编译就报错,person与string没有任何联系System.out.println("============");Student s3 = new Student();System.out.println(s3 instanceof Student); //trueSystem.out.println(s3 instanceof Person); //trueSystem.out.println(s3 instanceof Object); //true
// System.out.println(s3 instanceof Teacher); //编译报错,teacher与student无任何关系
// System.out.println(s2 instanceof String); //编译就报错,person与string没有任何联系}
}
类型转化
让我们举个栗子
除了测试类与instanceof中不一样,其余都一样,在这里就不重复了
public class Application {public static void main(String[] args) {//类型之间的转换 基本类型转换 高低(64 32 16 8)// 父 ------> 子// 高 ------> 低//当子类转换为父类,可能会丢失一些自己本来的方法Person s1 = new Student();
// s1.go; 这样无法直接调用,这是子类独有的方法,父类无法调用//s1将Person类型,也就是这个对象,转换为Student类型,就可以调用Student类型的方法((Student) s1).go();//这两个代码本质相同,下面这个代码是对上面代码最直观的解释Student s2 = (Student) s1;s2.go();}
}
/*
1.父类引用指向子类的对象
2.把子类转换为父类,向上转型:可以直接转换
3.把父类转换为子类,向下转型:需要进行强制类型转换(再基本类型中会丢失精度,在这里可能会丢失一些方法)
4.方便方法的调用,减少重复代码抽象 : 封装 继承 多态*/
static关键字详解
让我们举个栗子
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 p1 = new Person();System.out.println("=============");Person p2 = new Person();}/*静态代码块匿名代码块构造方法*/
}
package OOP.demo10;//static
public class Student {private static int age; //静态的变量 多线程private double score; //非静态变量public static void go(){}public void run(){go(); //非静态方法也可以这样直接调用静态方法}public static void main(String[] args) {new Student().run(); //通过new一个对象才能用一点去调用这个方法Student.go(); //go方法在这个类中,他是静态方法,可以直接使用类名去调用这个方法go(); //也可以猴这样直接调用该方法// Student s1 = new Student();
//
// System.out.println(Student.age);
//// System.out.println(Student.score); 无法使用类名加一点去调用
//
// System.out.println(s1.age);
// System.out.println(s1.score);}{//代码块(匿名代码块)}static {//静态代码块}}
package OOP.demo10;import java.sql.SQLOutput;import static java.lang.Math.random; //静态导入包
import static java.lang.Math.PI; //静态导入包public class Test {public static void main(String[] args) {System.out.println(random()); //输出一个随机数,math类中的System.out.println(Math.random()); //如果没有导包,得这么写System.out.println(PI);}
}
抽象类
abstract修饰符可以用来修饰方法也可以用来修饰类,如果修饰方法,那么该方法就是抽象方法,如果修饰类,那么该类就是抽象类
抽象类中可以没有抽象方法,但是有抽象方法的的类一定要声明为抽象类
在抽象类中,不能用new关键字来创建对象,他是用来让子类继承的
抽象方法,只有方法的声明,没有方法的实现,他是用来让子类实现的
子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类
我们直接来举个栗子
package OOP.demo11;//abstract 将该类变成抽象类
//extends java里是单继承,没有多继承,类只能单继承,但是接口可以多继承
public abstract class Action {//约束 有人帮我们实现//抽象方法,该方法没有实现的功能模块,只有一个方法名public abstract void doSomething();
}
再创建一个继承他的类
public class A extends Action {/*因为A继承Action,但是A为抽象类,其中有抽象方法我们在创建该类去继承的时候就会报错,要求我们必须重写其中被抽象的方法抽象类的所有方法,继承了它的子类,都必须实现它的方法*///除非它的子类也是abstrict抽象类,那就不用实现@Overridepublic void doSomething() {}
}
/*特点:1.不能new这个抽象类,只能通过子类去实现,他就是一个约束2.抽象类里可以有普通方法,但是抽象方法必须在抽象类中抽象的抽象:约束存在意义:一个角色的创建,可以是继承他的方法,然后将公有属性抽象出来,就可以是代码更简洁高效*/
接口的定义与实现
普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有
接口:只有规范,自己无法写方法,专业约束,约束和实现分离:面向接口编程
声明类的关键字是class,声明接口的关键字是interface
接口就是规范,定义的是一组规则,体现了现实生活中“如果你是...则必须能...”的思想,如果你是汽车,则必须能跑
接口的本质就是契约,就像现实中的法律,制定好后大家遵守
OOP的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式,模式都只针对具备了抽象的语言(c++,java,c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象
我们先创建两个接口
package OOP.demo12;//抽象的思维
//interface 接口的关键字,每个接口都需要有他的实现类
public interface UsersService {//接口中所有定义的属性都是一个静态常量//前面使用的关键字是public static finalint age = 64;//接口中的所有定义方法都是抽象的,其默认为public abstractvoid add(String name);void delete(String name);void update(String name);void query(String name);
}
package OOP.demo12;public interface TimeService {void timeer();
}
我们再创建一个接口的实现类
package OOP.demo12;//类可以实现接口,且命名为接口后加Impl,记得在类中写入implement去连接接口
//实现接口的类,必须要重写接口里所有的方法
//从侧面实现了多继承 利用接口
public class UserServiceImpl implements UsersService,TimeService { //该写法实现了多继承,他同时继承了UsersService,TimeService两个接口@Overridepublic void add(String name) {}@Overridepublic void delete(String name) {}@Overridepublic void update(String name) {}@Overridepublic void query(String name) {}@Overridepublic void timeer() {}
}
接口作用:
1.约束
2.定义一些方法,让不同的人实现
3.默认方法前的修饰词都是public abstract
,将其抽象了
4.我们定义一个常量,他前面的修饰词为public static final
5.接口不能被实例化,接口中没有构造方法
6.implement可以实现多个接口
7.实现接口必须要重写里面的方法
内部类
内部类就是在一个类的内部再定义一个类,比如,A中定义了一个B类,那么B类对于A类来说就是一个内部类,而A类相对B类来说就是外部类
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
我们直接来举个栗子
先创建一个外部类
public class Outer {private int id=5;public void out(){System.out.println("这是一个外部类的方法");}//class也可以写在方法中,这为局部内部类public void method(){class Inner{}}//当在这里加上static是,这个就变成静态内部类,下面就无法获取到id了public class Inner {public void in(){System.out.println("这是一个内部类的方法");}//获得外部类的私有属性,私有方法等public void getId(){System.out.println(id);}}}
//一个java类中可以有多个class类,但是只能有一个public class
class A{}
我们再建一个例子类
public class Test {public static void main(String[] args) {//匿名内部类//没有名字初始化类,不用将实例保存到变量中new Apple().eat();UserService userService = new UserService() {@Overridepublic void hello() {}};}
}class Apple{public void eat(){System.out.println("eat apple");}
}interface UserService{void hello();
}