面向对象
面向对象
面向过程和面向对象
面向过程的思想
- 步骤清晰简单,第一步做什么,第二步做什么
- 面对过程适合处理一些简单的问题
- 举例:老师教书,老师先进入教师,上课传输知识,下课
面向对象的思想
- 物以类聚,分类的思维模式,思考问题首先会解决问题需要那分类,然后对这些分类进行单独思考.最后,才对某个分类下的细节进行面向过程的思考
- 面向对象适合处理复杂的问题,适合处理需要多人协作的问题
- 举例:学生需要学习很多知识,就需要很多不同学科的老师来教学生知识,有很多教不同学科的人组成了老师这个分类
总结
对于描述复杂的事物,为了从宏观上把握,从整体上合理分析,我们需要使用面向对象的思路来分析整个系统.但是,具体的微观操作,仍然需要面向过程的思路去处理
回顾方法的定义和调用
方法的定义
语法
修饰符 返回值类型 方法名称(参数类型 参数名称) 异常抛出{
方法体
return 返回值;
}
修饰符:public 可以让任何类访问到;
private只有本类中可以使用
protected 可以让不同包中的类访问但是它们必须有继承关系
修饰符可以选择写,可以选择不写,默认default(不用把default写出来),意思就是只能由跟这个类在同一个包中的类来访问
返回值类型:可以为值类型或者引用类型,也可以返回值为void(不返回值,执行完方法就没了)
方法名称:按照驼峰命名法取名
参数类型:可以为值类型或者引用类型,也可以选择不写,有参数就是有参方法,没有则是无参方法
传递的参数分为:形参和实参 形参:由外界输入进来的数据 实参:实际要传过来的参数
参数可以为多个
方法体:想要执行的语句
return:返回的内容需要和返回值类型匹配一致
break是跳出循环和选择 return 是结束整个方法
异常抛出:当这个方法执行错误时,则会抛出你自己写的这个异常
方法的调用
-
静态方法
static void example(){ System.out.println("我是静态方法"); } public static void main(String[] args) { example(); //静态方法可以在当前类直接调用 } -
非静态方法
void example(){ System.out.println("我是非静态方法"); } public static void main(String[] args) { Demo3 demo3 = new Demo3(); //这个是new了一个类,使用对象调用里面的方法,new的是你自己创建的类 demo3.example(); } -
形参和实参
void example(String name){ //括号里面为形参 System.out.println(name); } public static void main(String[] args) { Demo3 demo3 = new Demo3(); //这个是new了一个类,使用对象调用里面的方法 demo3.example("我是实参"); //传值的时候传的是实参 } -
值传递和引用传递
值传递
值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量.
int a=1; //声明一个变量,赋值为1 int b=a; //把a的值赋给b b=20; //b把值改为20 System.out.println(a); //输出a,a的值没有被改变引用传递
引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。
一般认为,java内的基础类型数据传递都是值传递. java中实例对象的传递是引用传递public class Demo9 { //创建一个类,类里面声明一个nums变量 int nums; } Demo9 demo9 = new Demo9(); //实例一个demo9的变量 demo9.nums=100; //给demo9的变量赋值 Demo9 demo91=demo9; //实例一个demo91的变量 demo91.nums=500; //给demo91的变量赋值 System.out.println(demo9.nums); //输出demo9变量的值在这里可发现demo9.nums的值发生了变化,传递的是一个内存地址,而不是对象本身,由于这个对象的内存地址被占用,demo9.nums的值会因为demo91.nums的值跟随着改变
-
this关键字
package com.shuai.array; public class Demo3 { int num=0; //定义了一个成员变量 public static void main(String[] args) Demo3 demo3 = new Demo3(); //实例一个 demo3.example1();//使用对象名调用方法 } void example1(){ this.num=50; //使用this关键字设置num的值 System.out.println(num);//输出值 } }this关键字是指当前类的属性和方法,但是this不能在static下使用,
Static方法是类方法,先于任何的实例(对象)存在。即
Static方法在类加载时就已经存在了,但是对象是在创建时才在内存中生成
什么是面向对象
面向对象(Object Oriented Programming,OOP),面向对象是相对于面向过程来讲的,面向对象方法,把相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统建模,更贴近事物的自然运行模式
面向对象的编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据
抽象(抽象是从众多的事物中抽取出共同的、本质性的特征,而舍弃其非本质的特征的过程)
三大特性:
- 封装(你有很多钱,存储在银行卡,只能通过银行卡去取钱)
- 继承(这些钱都是来源于国家的创作机构)
- 多态(这些钱可以有多个用途)
从认识论角度考虑是先有对象后有类.对象,是具体的事物.类,是抽象的,是对对象的抽象
从代码运行角度考虑是先有类后有对象.类是对象的模板
类和对象的关系
-
类是一种抽象的数据类型,它是对某一事物整体描述/定义,但是并不能代表某一个人
动物,植物
这些都是用来描述/定义某一类具体的事物应该具备的特点和行为
-
对象是抽象概念的具体实例
狗,猫,草,桃树
能够体现出特点,展现出功能的是具体的实例,而不是一个抽象的概念
创建与初始化对象
使用new关键字创建对象
使用new关键字创建的时候,除了分配空间除外,还会给创建好的对象进行默认的初始化及对类中构造器的调用
语法
类 对象名=new 类();
实例:
//创建一个学生类
public class Student {
public String name; //姓名
public String sex; //性别
//学习方法
public void study(){
System.out.println(this.name+"在学习");
}
}
//主方法类
public class Application {
public static void main(String[] args) {
//实例化一个Student类 student对象就是具体的Student类的实例
Student student = new Student();
//使用student对象对name赋值
student.name="帅帅";
//使用student对象对sex赋值
student.sex="男";
//输出名字和性别
System.out.println(student.name);
System.out.println(student.sex);
//使用student对象调用Student类的study方法
student.study();
//假如你没有赋值,数据类型是有默认值
//数据默认值网址:https://www.cnblogs.com/ShuaiStudy/p/13969156.html
}
}
构造器
类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的.并且构造器有以下的两个特点:
- 必须和类的名字相同
- 必须没有返回类型,也不能写void
/**
* 人
*/
public class Person {
//名字
String name;
//无参构造方法
//在类创建的时候他就隐式的跟随着创建了
public Person() {
}
//有参构造方法
public Person(String name) {
//把这个类中的名字等于传进来参数的名字
this.name = name;
}
}
public class Application {
public static void main(String[] args) {
//当new关键字实例类的时候,他会隐式的去调用无参构造方法,无参构造方法在new的时候会自动生成
//假如类中存在有参构造方法,无参构造方法必须显示出来,不然就会报错
//使用这个方式创建对象,有参存在,无参要显示除开
Person person = new Person(); //这个他会调用无参构造方法
// Person person = new Person("帅帅"); //这个则是调用有参构造方法
System.out.println(person.name);
}
}
idea可以通过insert+alt要么就是fn+alt+insert,选择Constructor可以自动帮我们生成有参构造方法,如果是无参构造方法则是选择下面的Select None就可以了
创建对象内存分析

- 在运行的时候先把类和方法加载到方法区
- 在执行main()方法的时候栈区创建一个dog的引用变量名
- 在堆区给这个dog开辟内存地址,调用pet类里面的属性给他赋值
- 调用pet类的方法
- 最后输出所有的值
封装
在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。
封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
要访问该类的代码和数据,必须通过严格的接口控制。
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性
java程序都追求高内聚,低耦合
封装的优点
-
- 良好的封装能够减少耦合。
-
- 类内部的结构可以自由修改。
-
- 可以对成员变量进行更精确的控制。
-
- 隐藏信息,实现细节。
实现
一般属性都是用private修饰,表示私有的,只能在本类使用,而外部不能直接访问,外部使用一些公共的方法进行访问,如此就封装了一些属性和数据
通常情况下,这些方法被称为getter和setter方法
声明一个老师类,给老师类设置一些私有属性
package com.shuai.oop;
public class Teacher {
private String name; //名字
private int age; //年龄
private String sex; //性别
//使用get方法来取出属性的值
public String getName() {
return name;
}
//使用set方法来给属性赋值
//一般使用this指定当前属性
//解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
实例老师类,调用set方法设置值,get方法取出值
Teacher teacher = new Teacher();
teacher.setName("王老师"); //给name属性设置值
teacher.setAge(24);//给age属性设置值
teacher.setSex("女");//给sex属性设置值
System.out.println("姓名:"+teacher.getName()); //取出name值
System.out.println("年龄:"+teacher.getAge()); //取出age值
System.out.println("性别:"+teacher.getSex());//取出sex值
继承
概念
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
所以继承需要符合的关系是:is-a,父类(基类)更通用,子类(派生类)更具体,子类 is 父类
extends的意思式扩展,子类是父类的扩展,子类通过extends继承父类
注意点:java只有单继承,没有多继承,一个子类只能有一个父类,一个父类可以有多个子类
实现
//父类
public class Person {
private String name; //名字
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//输出name属性值的方法
public void print(){
System.out.println(name);
}
}
//子类
public class Student extends Person{
}
//实现类
public class Application {
public static void main(String[] args) {
Student student = new Student(); //实例的是学生类,但是可以调用父类的公共方法
student.setName("人"); //调用父类的set方法
System.out.println(student.getName()); //调用父类的get方法
student.print(); //调用父类的输出方法
}
}
子类就不会存在重复的代码,维护性也提高,代码也更加简洁,提高代码的复用性(复用性主要是可以多次使用,不用再多次写同样的代码)
继承的特性
- 子类拥有父类非 private 的属性、方法。
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
- Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
- 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
所有的类都是直接要么间接的继承Object类
super详解
super可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类
super指定的就是当前类的父类,可以访问父类所以的公有方法和属性
使用super直接调用父类属性(一般属性都是私有private的,所以一般都是调用方法)
//父类
public class Person{
public String name; //父类一个公有的name属性
}
//子类
public class Student extends Person {
public void print(){
super.name="Person"; //使用super.name设置父类的值
System.out.println(super.name); //输出super.name的值
}
}
//实现类
public class Test{
public static void main(String[] args) {
Student student = new Student(); //实例一个student的类的对象
student.print(); //调用本类的print方法
}
}
子类中的成员变量或方法与父类中的成员变量或方法同名
//父类
public class Person {
String name; //一个name属性
void setName(){ //一个给name属性赋值的方法
name="Person";
}
}
//子类
public class Student extends Person{
String name; //子类也有相同的name属性
void setName(){
name="Student"; //给子类赋值
super.setName(); //使用super调用父类的赋值方法
System.out.println(this.name); //this指定的是当前类的name属性
System.out.println(super.name); //super指定的是当前类父类的name属性
//使用这两个关键字容易区分
}
}
//主程序类
public class Application {
public static void main(String[] args) {
Student student = new Student(); //实例子类
student.setName(); //使用子类对象调用子类的方法
}
}
引用构造函数
//父类
public class Person{
public Person() {
System.out.println("父类无参构造方法");
}
public Person(String value) {
System.out.println("父类有参构造方法"+value);
}
}
//子类
public class Student extends Person {
public Student(){
super(); //调用super()必须写在子类构造方法的第一行
System.out.println("子类无参构造方法");
}
public Student(String value){
super("父类"); //调用super()必须写在子类构造方法的第一行
System.out.println("子类有参构造方法"+value);
}
}
//测试类
public static void main(String[] args) {
Student student = new Student(); //实例一个student的类的对象调用无参构造方法
Student student1 = new Student("子类"); //实例一个student的类的对象调用有参构造方法
}
this和super的异同
- super(参数):调用父类中的某一个构造函数(应该为构造函数中的第一条语句)
- this(参数):调用本类中另一种形成的构造函数(应该为构造函数中的第一条语句)
- super: 它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名(实参)
- this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量名)
- 调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。
- super()和this()类似,区别是,super()从子类中调用父类的构造方法,this()在同一类内调用其它方法。
- super()和this()均需放在构造方法内第一行。
- 尽管可以用this调用一个构造器,但却不能调用两个。
- this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
- this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。
- 从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。
方法重写
方法重写的必要条件
-
必须是在具有继承或者实现关系的两个类之间;
-
方法名、参数列表必须相同;
-
子类重写方法的返回值类型如果是基本数据类型必须和父类相同,如果是引用类型必须是父类方法返回值的子类或和父类的返回值相同;
-
重写方法若要抛出异常,必须是被重写方法抛异常类的异常子类或者相同或者和比被重写方法抛得异常要少,父类方法不抛异常子类重写也不抛异常;
-
重写方法的访问修饰符范围要大于等于被重写方法的访问修饰符。
测试
//父类
public class Person {
public void print(){
System.out.println("父类方法");
}
}
//子类重写父类方法
public class Student extends Person {
@Override
public void print() {
System.out.println("子类方法");
}
}
//测试
public class Application {
public static void main(String[] args) {
Student student = new Student(); //student对象
student.print();
}
}
多态
接口的多种不同的实现方式即为多态。
我们在程序中定义的引用变量所指向的具体类型和通过该引用变量的方法调用在编程的时候并不确定,当处于运行期间才确定。就是这个引用变量究竟指向哪一个实例对象,在编译期间是不确定的,只有运行期才能确定,这样不用修改源码就可以把变量绑定到不同的类实例上,让程序拥有了多个运行状态,这就是多态
多态实现的必要条件
- 继承
- 重写
- 父类引用指向子类对象
实例
//父类
public class Person {
public void print(){
System.out.println("父类方法");
}
}
//子类重写父类方法
public class Student extends Person {
@Override
public void print() {
System.out.println("子类方法");
}
}
//测试
public class Application {
public static void main(String[] args) {
//向上转型
Person student = new Student(); //student对象
student.print();
Person person = new Person(); //person对象
person.print();
}
}
在上面的例子中可以看到,尽管 student 属于 Person类型,但是它运行的是 Student类的 print方法。
这是由于在编译阶段,只是检查参数的引用类型。
然而在运行时,Java 虚拟机(JVM)指定对象的类型并且运行该对象的方法。
因此在上面的例子中,之所以能编译成功,是因为 Person类中存在 print方法,然而运行时,运行的是特定对象的方法
如果父类没有子类的方法,Person student = new Student();调用子类方法则会报错
如果父类和子类都有同样的方法,则会先调用new 后面实例类的方法
instanceof(这个对象是否是这个特定类或者是它的子类的一个实例)
System.out.println(student instanceof Student);//true
System.out.println(student instanceof Person); //true
System.out.println(student instanceof Object); //true
这个代码是在上面的测试类写的,但是需要上面的子类和父类
- 第一个student对象的实例就是Student类,所以是正确的
- 第二个student的对象由于继承了Person类,所以是正确的
- 第三个student的对象由于java所以的类都是间接和直接继承Object类
引用类型转换
这个代码是在上面的测试类写的,但是需要上面的子类和父类
//实例一个学生类
Student student = new Student();
//将学生类的引用类型向上转型变成人类的引用类型
//向上转型 就是把子类的对象指向父类的引用
Person person=student;
//调用方法
person.print();
//将人类的引用类型向下转型变成学生类的引用类型
//向下转型 就是把父类的对象指向子类的引用
Student person1=(Student) person;
person1.print();
如果是两个类型不相关的类转换,会编译报错
static关键字详解
static表示静态的,修饰方法和属性,都会在解析的时候被创建出来,跟着类一起加载
static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。这实际上正是static方法的主要用途
//做为修饰属性,可以直接在本类使用
public class Test {
static int age=100;
public static void main(String[] args) {
System.out.println(age);
}
}
//做为修饰方法,可以直接在本类使用
public class Test {
static void print(){
System.out.println("静态方法");
}
public static void main(String[] args) {
print();
}
}
//做为静态代码块,跟着类一起创建
public class Test {
{
System.out.println("隐藏代码块");
}
static {
System.out.println("静态代码块");
}
public static void main(String[] args) {
Test test = new Test(); //在newd的时候才调用隐藏代码块
}
}
//做为静态导入包,导入随机数的包
import static java.lang.Math.random;
public class Test {
public static void main(String[] args) {
System.out.println(random());
}
}
抽象类
Java语言中,用abstract 关键字来修饰一个类时,这个类叫作抽象类,修饰一个方法叫做抽象方法。抽象类是它的所有子类的公共属性的集合,是包含一个或多个抽象方法的类。抽象类可以看作是对类的进一步抽象。在面向对象领域,抽象类主要用来进行类型隐藏
public abstract class Demo3 { //抽象类
public void print(){ //输出方法
System.out.println();
}
public abstract void run(); //抽象跑方法
}
注意点:
- 抽象方法不能有方法体,如果有抽象方法,类必须为抽象方法
- 定义了抽象类,里面可以没有抽象方法,不是抽象方法可以有方法体
- 抽象类不能被实例,只能交给子类来继承
- 抽象方法只能通过子类来实现,他就声明方法
public class Demo4 extends Demo3 {
@Override
public void run() {
}
}
注意点:
- 当一个类继承了抽象类,就必须实现抽象类中的抽象方法
接口
接口就是规范,定义了一组规则,接口的本质是契约,就像人间法律一样,制定好大家遵守
定义类的关键字是class,定义接口关键字则是interface
public interface method {
void print();
}
public class Test implements method {
@Override
public void print() {
}
}
注意点:
- 通过implements关键字来实现接口,可以实现多个接口,单继承,多实现
- 接口不能被实例,只能被实现,接口中没有构造方法,而且不能有方法体
- 实现接口的类必须重写父类的方法
内部类
内部类就是在一个类的内部在定义一个类,比如a类中定义了一个b类,那么a就是外部类,b就是内部类
内部类
public class Test { //外部类
public static void main(String[] args) {
new com.shuai.oop.inherit.Out().print(); //通过包名来实例类和调用方法
}
}
//在类外面在创建一个类,下面可以有多个类,但是不能修饰符为public
//这个类属于在src目录下的类
class Out{
public static void print(){ //一个输出方法
System.out.println("输出外部类");
}
}
成员内部类
public class Test { //外部类
class Out{ //内部类
public void print(){ //一个输出方法
System.out.println("输出内部类方法");
}
}
public static void main(String[] args) {
Test test = new Test(); //通过实例本来的外部类
Out out = test.new Out(); //在使用外部类的对象去new内部类的实例
out.print(); //调用内部类的方法
}
}
静态内部类
public class Test { //外部类
static class Out{ //内部类
public void print(){ //一个输出方法
System.out.println("输出内部类方法");
}
}
public static void main(String[] args) {
new Out().print(); //通过new静态类来调用静态内部类方法
}
}
局部内部类
public class Test
{
public static void main (String[] args)
{
Test t = new Test(); //实例主类
MyInner inner = t.getInner(); //调用实例局部内部类的方法
inner.innerTest(); //返回实例完的结果,调用里面的方法
}
public MyInner getInner() { //定义一个实例局部内部类的方法
class Inner implements MyInner{ //创建一个局部内部类实现
public void innerTest() { //重写测试的方法
System.out.println("innerTest"); //输出测试值
}
}
return new Inner(); //返回局部内部类实例
}
}
interface MyInner{ //定义一个接口
void innerTest(); //定义一个测试的方法
}
匿名内部类
public class Test
{
public static void main (String[] args)
{
//没有名字初始化类,不用讲实例保存到变量中
new Dog().eat(); //指向的引用类型不确定
}
}
class Dog {
public void eat(){
System.out.println("吃");
}
}
注意点:
- 内部类可以有多个,但是不能使用public修饰,如果实在主类的里面可以使用public修饰
- 内部类定义在主类外面,就是在整个java文件下,使用全路径来访问,而且不能使用static
- 在主类中的成员属性,在静态内部类中不能存在主类中的成员属性,因为静态内部类在创建的时候跟着创建了
- 局部内部类不能直接调用

浙公网安备 33010602011771号