面向对象
1.概念
- Java的核心思想就是面向对象编程(OOP)其中OO是面向对象
- 属性+方法=类
- 面向对象编程的本质:以类的方式组织代码,以对象的组织(封装)具体数据。
- 抽象:把共同点剥离出来组成类
- 三大特性
- 封装
- 继承
- 多态
- 对象是具体的,类是抽象的
- 从代码角度考虑:先有类,再有对象。类是对象的模板
- 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认初始化,以及对类中构造器的调用(构造器必须掌握)
2.简单的创建类及调用
package com.oop.demo02;
//学生类
public class Student {
//一个类只会有两种东西:属性和方法
//属性:字段
String name;
int age;
//方法
public void study(){
System.out.println(this.name+"在学习");//this代表当前类
//可以引用当前类中的属性
}
}
//比如person类 --->身高,体重,年龄等属性
/*
//测试类
public static void main(String[] args) {
//类:抽象的,需要实例化
//类实例化后会返回一个自己的对象
//xiaoming对象就是一个Student类的具体实例
Student xiaoming = new Student();
Student xh = new Student();
xiaoming.name = "小明";
xiaoming.age = 3;
xh.name = "小红";
xh.age = 5;
System.out.println(xiaoming.name);
System.out.println(xiaoming.age);
System.out.println(xh.name);
System.out.println(xh.age);
}
*/
小明
3
小红
5
Process finished with exit code 0
3.有参无参构造器
- 类中构造器也称为构造方法,是在进行创建对象的时候必须要调用的,并且构造器有以下两个特点
※ 必须和类的名字相同
※ 必须没有返回类型,也不能写void - 注意事项
- Alt+insert / Constructor自动生成构造器 (可以选无参和有参)
- 一个类即使什么都不写,也会存在一个方法
- 使用new关键字必须要有构造器,new本质是在调用构造器
- 构造器一般用来初始化值
- 有参构造;一旦定义了有参构造,就必须显示定义一个无参(无参里空着)
- 生成构造器快捷键 Alt+Insert
- 单例模式需要构造器私有(有疑问)
//这就是一个无参构造
public Person() {
}
//这是一个有参构造
public Person(String name, int age) {
this.name = name;
this.age = age;
}
一个测试实例
package com.oop.demo02;
//java----->class
public class Person {
//一个类即使什么都不写,也会存在一个方法
String name;
int age;
//实例化初始值,这是一个默认的无参构造器
//1.使用new关键字必须要有构造器,new本质是在调用构造器
//2.构造器一般用来初始化值
public Person(){
//this.name = "kong";
}
//有参构造;一旦定义了有参构造,就必须显示定义无参(无参里空着)
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
/*
//测试类
public class Application {
public static void main(String[] args) {
//使用new关键词实例化一个对象
Person person = new Person("debao",3);
System.out.println(person.name+" "+person.age+"岁");//debao
}
*/
运行结果
debao 3岁
Process finished with exit code 0
4.创建对象内存分析
基础65集
- 代码
package com.oop.demo03;
public class Pet {
public String name;
public int age;
//如果不写,默认有无参构造
//如果写了有参构造,就不会默认有无参构造
public void shout(){
System.out.println("叫了一声");
}
}
/*
//测试类
public static void main(String[] args) {
Pet dog = new Pet();//dog是引用变量名,指向堆中具体的对象
dog.name = "旺财";
dog.age = 3;
Pet cat = new Pet();
cat.name = "如花";
cat.age = 2;
dog.shout();
System.out.println(dog.name);
System.out.println(dog.age + "岁了");
System.out.println("=========");
System.out.println(cat.name);
System.out.println(cat.age + "岁了");
}
*/
- 运行结果
叫了一声
旺财
3岁了
=========
如花
2岁了
Process finished with exit code 0
- 内存分析
- 方法区也属于堆
- 首先加载Application类到方法区
- 执行main()方法,将main()压入栈底
- new一个Pet
- 加载Pet类到方法区
- 生成一个具体对象dog,dog是在栈里面的,只是一个引用变量名(真实的对象是在堆里面)
- 栈存放引用,堆存放具体的对象
- 真正的对象是在堆里面的,通过Pet模板去new了一个Pet
- 此时属性都是默认值
Pet dog = new Pet();//dog是引用变量名,指向堆中具体的对象
- 给name和age赋值
dog.name = "旺财";
dog.age = 3;
- 同理,再来一只小猫(方法区也属于堆)
5.类与对象小结
类与对象
类是一个模板,对象是一个具体的实例
方法
定义,调用!
对象的引用
- 引用类型:八大基本类型
- 对象是通过引用来操作的:栈----->堆
属性:字段Field 成员变量
- 默认初始化
- 数字: 0 0.0
- char:u0000
- boolean:false
- 引用:null
- 修饰符 属性类型 属性名 = 属性值
对象的创建和使用
- 必须使用关键词创建对象,构造器 Preson kong = new Preson();
- 对象的属性 kong.name
- 对象的方法 kong.sleep()
类:
- 静态的属性 属性
- 动态的行为 方法
后续学习
- 封装
- 继承
- 多态
6.封装
概念
- 通常,应禁止直接访问一个对象中的数据的实际表示,而应该通过操作接口(get/set)来访问,这称为信息隐藏
思想
- 该露的露,该藏的藏
- 追求”高内聚,低耦合“。高内聚:就是类的内部操作细节自己完成,不允许外部干涉,低耦合:仅暴露少量的方法给外部使用
- 记住这句话:属性私有,get/set
- 私有属性不能直接调用,需要用get/set方法
- Alt+Insert可以自动生成get/set属性
封装的意义
- 提高代码安全性,保护数据
- 隐藏代码的实现细节
- 统一接口
- 系统可维护性增加
私有封装实例
package com.oop.demo04;
//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;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
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;
}
}
// 学习(),睡觉()
}
/*
//测试类
package com.oop;
import com.oop.demo03.Pet;
import com.oop.demo04.Student;
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
s1.getName();//通过getName方法获取私有属性
s1.setName("孔");//通过setName设置属性值
System.out.println(s1.getName());
//设置年龄
s1.setAge(50);
System.out.println(s1.getAge());
}
}
*/
7.继承
基础知识
- 继承本质是对某一批类的抽象,从而对现实世界更好的建模。
- Java中类只有单继承,没有多继承(一个儿子只能有一个爸爸,但是一个爸爸可以有多个儿子)
- 继承是类和类之间的关系,除此之外,类和类之间的关系还有依赖,组合,聚合等。
- 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。
- 子类和父类之间,从意义上讲应具备有“is a”的关系
- extends的意思是扩展,子类是父类的扩展,用法如下
package com.oop.demo05;
//Person 人 /父类/基类
public class Person {
}
package com.oop.demo05;
//Student is a Person /派生类/子类
//extends 继承
public class Student extends Person{
}
子类继承父类
- 子类继承父类就会拥有父类的全部方法
- 只可以继承公共的方法public,不可以继承private(一般继承方法都是public,属性是private)
- 几种修饰符的优先级
- public
- protected 受保护的
- default 默认权限
- private
- 例子:子类
package com.oop.demo05;
//Student is Person
//extends 继承
public class Student extends Person{
public static void main(String[] args) {
Student student = new Student();
student.say();
System.out.println(student.money);
}
}
- 例子:父类
package com.oop.demo05;
//Person 人
public class Person {
public int money = 10_0000_0000;
public void say(){
System.out.println("说了一句话");
}
}
- 运行结果
说了一句话
1000000000
Process finished with exit code 0
- Object类
- 快捷键Ctrl + H打开查看继承树
- 在Java中,所有的类都默认直接或者间接继承Object
public class Person /*extends Object*/{
}//是否声明继承Object类都无所谓,会自动继承
super和this的注意点
- super注意点
- super调用父类的构造方法时,必须在构造方法的第一个
- super必须只能出现在子类的方法或者构造方法中!
- super和this不能同时调用构造方法
- super和his异同
- 代表的对象不同
this:本身调用者这个对象
super:代表父类对象的应用 - 前提
this:没有继承也可以使用
super:只能在继承条件下使用 - 构造方法
this:本类的构造
super:父类的构造
super和this的一般应用
//测试类
package com.oop;
import com.oop.demo03.Pet;
import com.oop.demo05.Student;
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.test("德宝");
}
}
//子类
package com.oop.demo05;
//Student is Person
//extends 继承
public class Student extends Person{
private String name = "debao";
public void test(String name){
System.out.println(name); //形参
System.out.println(this.name); //此类中的name,debao
System.out.println(super.name);//父类中的name,kong
}
}
//父类
package com.oop.demo05;
//Person 人
public class Person /*extends Object*/{
protected String name = "kong";
}
测试结果
德宝
debao
kong
Process finished with exit code 0
Note:私有的方法或属性无法被继承
super和this构造方法的应用
- 只要写了有参构造后,无参构造就没了;
- 父类只有有参,没有无参的话,子类也是不能写无参的;
- 所以写有参的话,必须写一个无参,无参可以是空的;
//测试类
package com.oop;
import com.oop.demo03.Pet;
import com.oop.demo05.Student;
public class Application {
public static void main(String[] args) {
Student student = new Student();
}
//子类
package com.oop.demo05;
//Student is Person
//extends 继承
public class Student extends Person{
//这个地方默认调用了父类的无参构造:super();
//这个super()如果要写出来,必须要写在子类构造器第一行
//构造器中super()和this()不能同时调用
public Student() {
System.out.println("Student无参构造执行了");
}
}
//父类
package com.oop.demo05;
//Person 人
public class Person /*extends Object*/{
public Person() {
System.out.println("Person无参构造执行了");
}
}
运行结果
Person无参构造执行了
Student无参构造执行了
Process finished with exit code 0
方法的重写(区别于重载)
- 重写与重载
重载是参数名相同,参数列表不同;重写是子父类才有,子类重写父类的方法
- 重写:需要有继承关系,子类重写父类的方法
- 方法名必须相同
- 参数列表必须相同
- 修饰符:范围可以扩大,但不能缩小; public>protested>default>private
- 抛出的异常:范围可以被缩小,但不能扩大;Exception(大) -->ClassNotFoundException
- 重写定义:子类的方法和父类必须要一致;方法体不同
- 为什么要重写:
- 父类的功能,子类不一定需要,或者不一定满足
- 快捷键 Alt + Insert
- 静态方法的重写
- 静态方法是类的方法,而非静态方法是对象的方法,有static时,b调用了B类的方法,因为b是用类B定义的,没有static时,b调用的是对象的方法,而b是用A类new的,即b是A new出来的对象,因此调用A方法。
- 非静态方法的重写
子类重写了父类的方法,执行子类的方法
8.多态
- 概念:同一个方法可以根据发送对象不同而采取多种不同的行为方式
- 一个对象的实际类型是确定的,但可以指向对象的引用类型有很多
- 多态注意事项
- 多态是方法的多态,属性没有多态;
- 必须是父类和子类,有联系,否则类型转换异常!ClassCastException!
- 存在条件:
- 继承关系
- 方法需要重写(比如父类子类都有run()方法,要执行哪一个呢?)
- 父类的引用指向子类 Father f1 = new Son()
- 有些方法不能被重写
- static是静态方法,属于类,不属于实例(对象)
- final 常量
- private 私有方法
9.instanceof和类型转换
instanceof的应用
主要判断x与y是否存在父子关系
System.out.println(x instanceof y);
//这个代码是不是能编译通过,主要取决x与y是否存在父子关系
//结果是ture还是false,主要看x所指向的实际类型是不是y的子类型
- 例子
package com.oop;
import com.oop.demo07.*;
public class Application {
public static void main(String[] args) {
/*
各类的关系说明
Object是任何类的父类
Person是父类
Student是子类
Teacher是子类
*/
Object object = new Student();
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("==============================");
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没有半毛钱联系,所以报错
Student student = new Student();
System.out.println("==============================");
System.out.println(student instanceof Student);//ture
System.out.println(student instanceof Person);//ture
System.out.println(student instanceof Object);//ture
//System.out.println(student instanceof Teacher);//编译报错 Student和Teacher同级,不能比较,所以报错
//System.out.println(student instanceof String);//编译报错 person和String没有半毛钱联系,所以报错
}
}
- 运行结果
true
true
true
false
false
==============================
true
true
true
false
==============================
true
true
true
Process finished with exit code 0
类型转换
基本的类型转换
- 类型优先级 低-----高
- byte<short<char<int<long<float<double //小数优先级一定大于整数
- 强制类型转换(高到低)
int i=128;
byte b=(byte)i;
//强制转换 (类型)变量名;
- 自动类型转换(低到高)
int i=128;
double b=i;
- 不能对boolean转换
- 不能转换为不相关类型
- 高—>低强制转换
- 可能出现内存溢出或者精度损失
方法的类型转换
- 总结
- 只能父类引用指向子类的对象
- 把子类转换为父类(向上转型),不用强制转换,可能丢失一些方法;
- 把父类转换为子类(向下转型),强制转换
- 方便方法的调用,减少重复的代码!简洁!
- 子类(低)----->父类(高)
子类转父类可能丢失自己本来的一些方法
package com.oop;
import com.oop.demo07.*;
public class Application {
public static void main(String[] args) {
/*
各类的关系说明
Object是任何类的父类
Person是父类 有run()方法
Student是子类 有go()方法
Teacher是子类
*/
Student a = new Student();
a.go();
Person b = a;
b.go(); //报错!!! b已经是Person类,不能调用Student方法,
//子类转父类可能丢失自己本来的一些方法
}
}
- 父类(高)---->子类(低)-----强制转换
package com.oop;
import com.oop.demo07.*;
public class Application {
public static void main(String[] args) {
/*
各类的关系说明
Object是任何类的父类
Person是父类 有run()方法
Student是子类 有go()方法
Teacher是子类
*/
Person a = new Student();
//a.go(); //Person父类执行Student子类的独有方法,报错
//将Person类型的a转化为Student类型,就可以用Student类型的方法了 高---->低
//Student a1 = (Student) a;//强制转换 方法一
((Student)a).go(); //强制转换 方法二
}
}
10.static关键字详解
注意:
- 通过final修饰的类不能被继承,没有子类(断子绝孙)
- static与类一起加载
- static静态属性
package com.oop.demo08;
public class Student {
//静态属性
private static int age; //静态变量
private double score; //非静态变量
public static void main(String[] args) {
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静态方法
- 非静态方法里面可以直接调用静态方法
- 静态方法里不能直接调用非静态方法
- 因为static和类一起加载
package com.oop.demo08;
public class Student {
//方法
//非静态方法
public void run(){
go();
}
//静态方法
public static void go(){
}
public static void main(String[] args) {
//run不可以直接调用
new Student().run();
//go可以直接调用
go();
}
}
- 代码块
package com.oop.demo08;
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 person1 = new Person();
System.out.println("=======================");
Person person2 = new Person();
}
}
- 运行结果
静态代码块
匿名代码块
构造方法
=======================
匿名代码块
构造方法
Process finished with exit code 0
- 导入包
package com.oop.demo08;
import static java.lang.Math.PI;
public class Test {
public static void main(String[] args) {
System.out.println(Math.random());//返回随机数
System.out.println(PI);
//不想写Math的话,可以把Math包导进去
//import java.lang.Math
//也可以导入里面的方法
//import static java.lang.Math.random;叫静态导入包
//然后就可以直接调用
//System.out.println(random())
}
}
- 运行结果
0.11463553175833108
3.141592653589793
Process finished with exit code 0
11.抽象类
注意:
- abstract修饰符可以用来修饰方法,也可以用来修饰类;如果修饰方法,那么该方法就是抽象方法,如果修饰类,那么该类就是抽象类;
- 抽象类中可以没有抽象方法,可以写普通方法,但抽象方法的类一定要声明为抽象类;
- 抽象类,不能使用new关键字来创建对象,他是用来让子类继承的;
- 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的;
- 子类继承抽象类,必须要实现抽象类中没有实现的抽象方法,否则该子类也声明为抽象类;
- 抽象类也是类,类只能单继承,但是接口可以多继承!
作业:
- 既然不能new,那存在构造器吗:存在
- 存在的意义:把功能抽象出来,提高开发效率和可扩展性
- 这是一个抽象类
package com.oop.demo09;
//abstract抽象类
public abstract class Action {
//是一种约束~可以后续有人帮我们实现~
//abstract~ 抽象方法~ 只有方法名字,没有方法的实现
public abstract void doSomething();
}
- 子类继承抽象类,必须要实现抽象类中没有实现的抽象方法,除非该子类也声明为抽象类
package com.oop.demo09;
//子类继承抽象类,必须要实现抽象类中没有实现的抽象方法,除非该子类也声明为抽象类;
public abstract class A extends Action{
}
----------------------------------------------------------------------------------------
package com.oop.demo09;
public class B extends Action{
//子类继承抽象类,必须要实现抽象类中没有实现的抽象方法,除非该子类也声明为抽象类;
@Override
public void doSomething() {
}
}
12.接口
重点:锻炼抽象思维
总结:
- 接口是约束
- 定义一些方法,让不同的人实现
- 接口中的所有定义其实都是抽象的 都默认加了 public abstract
- 所以定义的属性默认为静态常量public static final
- 接口不能直接被实例化~因为接口中没有构造方法
- implements可以实现多个接口
- 子类实现接口,必须要重写接口中的方法~
- 只有一个方法的接口叫做函数式接口,可以使用lambda表达式简化
- 定义接口,用interface关键词
package com.oop.demo10;
//interface 定义接口的关键字,接口都需要有实现类
public interface UserService {
//接口中的所有定义其实都是抽象的 都默认加了 public abstract
//接口中不能定义具体的方法
//接口中可以定义为变量,常量
//所以定义的属性默认为静态常量public static final
public static final int AGE = 99;
void add(String name);
void delete(String name);
void updata(String name);
void query(String name);
}
package com.oop.demo10;
public interface TimeService {
void timer();
}
- 实现接口功能的类:用implements关键词
Java是单继承,只能继承一个父类,但是一个类可以实现多个接口
package com.oop.demo10;
//抽象类:只能用extents
//类,可以实现接口 通过implements关键词 接口
//实现了接口的类,就需要重写接口中的方法
//利用接口实现多继承~
public class UserServiceImpl implements UserService,TimeService{
@Override//重写
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void updata(String name) {
}
@Override
public void query(String name) {
}
@Override
public void timer() {
}
}
13.内部类
定义:内部类就是在一个类的内部再定义一个类,比如,A类中定义一个B类,那么B类相对于A类来说就成为内部类,而A类相对于B类来说就是外部类。
注意:
- 内部类如果是static静态类且外部类是非静态,则不能调用外部类的属性,因为静态类很早就开始运行了
- 外部类和内部类
package com.oop.demo11;
public class Outer {
private int id;
public void out(){
System.out.println("这是外部类的方法");
}
public class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
//好处:可以获得外部类的私有属性~私有方法
public void getID(){
System.out.println(id);
}
}
}
/*
测试类
package com.oop;
import com.oop.demo11.Outer;
public class Application {
public static void main(String[] args) {
//new
Outer outer = new Outer();
//通过外部类来实例化内部类~
Outer.Inner inner = outer.new Inner();
inner.in();
}
}
*/
- 其他的写法
package com.oop.demo11;
public class Outer {
}
//一个java类中可以有多个class类,但只能有一个public类
//这也算一个内部类
class A{
public static void main(String[] args) {
}
}
- 局部内部类
package com.oop.demo11;
public class Outer {
//局部内部类:方法中的类
public void method(){
class Inner{
public void in(){
}
}
}
}
- 匿名内部类(重点):没有名字去初始化类,不用将实例保存在变量中~
package com.oop.demo11;
public class Test {
public static void main(String[] args) {
//实例化一个对象
Apple apple = new Apple();
//没有名字去初始化类,不用将实例保存在变量中~
new Apple().eat();
//也可以用这个想法去new接口
UserService userService = new UserService(){
@Override
public void hello() {
}
};
}
}
class Apple{
public void eat(){
System.out.println("eat");
}
}
interface UserService{
void hello();
}