十四、面向对象
万物皆对象,客观存在的事物皆是对象
1、类的实例化
一个简单的类
public class Dog {
/**
* 尺寸
*/
int size;
/**
* 颜色
*/
String coulour;
/**
* 爱吃啥
*/
public static void eat() {
System.out.println("爱吃肉骨头");
}
}
实例化这个类
public class DogTest {
@Test
public void test01() {
//实例化Dog这个类
//dog 是 Dog类实例化的对象
Dog dog = new Dog();
}
@Test
public void test02() {
//实例化Dog这个类
//dog01和dog02 是 Dog类实例化的对象
//但是两个对象是不同的
Dog dog01 = new Dog();
Dog dog02 = new Dog();
//输出false
System.out.println(dog01 == dog02);
}
}
2、成员变量与局部变量
成员变量 在类中定义,用来描述对象将来要有什么 在类中定义,在方法中临时保存 没有赋值,为初始值 与局部变量的区别 a)作用域不同 -局部变量的作用域仅限于定义它的方法 -成员变量的作用域在整个类内部都是可见的 b)初始值不同 -成员变量有默认的初始值 -局部变量没有默认的初始值,必须自行设定初始值 c)同名变量 -在同一个方法中不允许有同名的局部变量 -在不同的方法中可以有同名的而局部变量 d)存储位置不同 -成员变量在对象创建后存储在堆中,对象回收后消失 -局部变量是在方法被调用时存在于栈中,方法调用结束,从栈中消失 e)生命周期不同 -随着对象的创建而创建,对象回收后消失 -随着方法的调用而创建,随着方法执行结束而消失
静态成员变量实现了多个方法之间数值的共享
public int x;
//静态成员变量实现了多个方法之间数值的共享
public static int y;
public void fun01() {
System.out.println("非静态x" + ++x);
System.out.println("静态y" + ++y);
}
@Test
public void test01() {
Person03 person01 = new Person03() ;
Person03 person02 = new Person03() ;
//x-1
//y-1
person01.fun01();
//x-1
//y-2
person02.fun01();
//x-2
//y-3
person01.fun01();
//x-2
//y-4
person02.fun01();
}
3、方法的调用
/**
* 静态方法
*/
public static void sleep() {
System.out.println("我在睡觉");
}
public static void eat() {
System.out.println("吃薯片");
}
public void drink() {
System.out.println("喝可乐");
}
public class Person01Test {
@Test
public void test01() {
//静态方法可以用类名.方法名的方式调用
//但是不能通过该方式调用非静态方法
Person01.sleep();
}
@Test
public void test02() {
//静态方法可以用类名.方法名的方式调用
//但是不能通过该方式调用非静态方法
Person01.sleep();
//可以通过类的实例化对象.方法名的方式调用静态方法
//也可以通过类的实例化对象.方法名的方式调用非静态方法
Person01 person01 = new Person01();
person01.drink();
person01.eat(); //但没必要,一般通过类名.方法调用静态方法
}
}
4、匿名对象与引用对象
匿名对象
-
没有指向,用完之后立刻消失
引用对象
-
person04 引用类型变量 引用了内存中的一个对象
-
可能会多次调用,不会立即删除
三种变量的区别
普通变量 直接赋值 引用对象 引用类型变量 引用了内存中的一个对象 引用了一个内存地址 匿名对象 没有引用类型变量指向的对象 -优点 使用之后直接从内存中消失,不会长期占用内存,适用于仅仅偶尔使用的场景下 -缺点 因为使用之后直接消失,所以当需要频繁使用该对象时,会频繁的创建及销毁,创建和回收过程都需要占用系统资源 频繁使用的对象建议使用引用类型变量接受 一个简单的例子
public class Person04 {
public void laugh() {
System.out.println("仰天长笑");
}
}
@Test
public void fun01() {
/**
* 引用对象
* person04 引用类型变量 引用了内存中的一个对象
* 可能会多次调用,不会立即删除
*/
Person04 person01 = new Person04();
person01.laugh();
/**
* 匿名对象
* 没有指向,用完之后立刻消失
*/
new Person04().laugh();
}
5、对象的生成、赋值与读取
一个简单的类
public class Person01 {
/**
* 姓名
*/
String nickname;
/**
* 性别,1→男 0→女
* 默认值为0,女
*/
int gender;
/**
* 年龄
*/
int age;
}
生成它的对象
@Test
/**
* 生成对象
*/
public void test01() {
//实例化该类,生成对象
//类的指向 对象名 = new(初始化)实例化的类
Person01 person01 = new Person01();
Person01 person02;
person02 = new Person01();
}
生成对象的赋值与读取
@Test
/**
* 生成对象的赋值与读取
*/
public void test02() {
Person01 person01 = new Person01();
Person01 person02 = new Person01();
//给生成对象赋值
person01.nickname ="张三";
person02.nickname ="李四";
System.out.println(person01.nickname);
System.out.println(person02.nickname);
//person01.gender = 0; 0是默认值,无需再次赋值
person02.gender = 1;
System.out.println(person01.gender == 1 ? "男" : "女");
System.out.println(person02.gender == 1 ? "男" : "女");
person01.age = 20;
person02.age = 22;
System.out.println(person01.age);
System.out.println(person02.age);
}
6、访问修饰符
访问修饰符 public protected default private
public : 对所有类可见 使用对象:类、接口、变量、方法
protected : 对同一个包内的类和所有子类可见。使用对象:变量、方法。 注意:不能用来修饰类(外部类)
default(即默认,什么也不写,这个不能写) : 在同一个包内可见,不适用任何修饰符,使用对象:类、接口、变量、方法
private :只在同一个类内可见。使用对象:方法、变量。 注意:不能用来修饰类(外部类)
7、封装
-
良好的封装能够减少耦合。
-
类内部的结构可以自由修改。
-
可以对成员变量进行更精确的控制。
-
使用私有修饰符,隐藏信息,实现细节。
封装一个类
public class Person02 {
/**
* 姓名
*/
private String nickname;
/**
* 性别,1→男 0→女
* 默认值为0,女
*/
private int gender;
/**
* 年龄
*/
private int age;
//private修饰了变量,需要用的set和get来进行赋值和调用
//右键→source(Alt + Shift + S)→Generate getters and setters
//开发工具自动生成get和set
public void setAge(int a) {
if(a<=0 || a>200) {
System.out.println("年龄错误");
}else {
age = a;
}
}
public void setNickName(String a) {
//注意使用String nickname参数列表(即使用原变量名)时
//不能直接nickname = nickname;
//因为都是成员变量,没有赋值,此时实例化后对象参数都是默认值
//需要this.nickname=nickname;
//this.调用成员变量
//this 方法的调用者谁调用我 我就是谁
nickname=a;
}
public void setGender(int a) {
gender = a;
}
public int getGender() {
return gender;
}
public int getAge() {
return age;
}
public String getNickName() {
return nickname;
}
}
调用该封装好的类
public class Person02Test {
@Test
/**
* 生成对象
*/
public void test01() {
//实例化
Person02 person01 = new Person02();
//赋值
person01.setNickName("法外狂徒张三");
person01.setGender(1);
person01.setAge(20);
//读取
System.out.println("姓名是:" + person01.getNickName());
System.out.println("性别是:" + (person01.getGender()== 1 ? "男" : "女"));
System.out.println("年龄是:" + person01.getAge());
}
}
8、构造方法
方法名与类名相同,方法体没有返回值,但是在声明的时候却不需要使用void修饰的方法称之为构造方法
作用:用于初始化对象,对象的属性赋值
没有参数的方法称之为无参构造方法
代码中没有编写过构造方法,jvm在执行对象初始化的帮助程序追加无参构造方法
一个类中,既没有无参构造方法也没有有参构造方法的时候jvm会追加一个无参构造方法
一个类中,存在有参构造方法没有编写无参构造方法的时候jvm不会追加无参构造方法
不论任何情况下一定要手写一个无参构造方法
在类的实例化过程中被调用,实例化几次调用几次
无参构造方法
public class Person01 {
public Person01() {
}
}
全参构造方法
public class Person02 {
//昵称
private String nickname;
//性别 1男0女
private int gender;
//年龄
private int age;
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public int getGender() {
return gender;
}
public void setGender(int gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/**
* 全参构造方法
* @param nickname 昵称
* @param gender 性别
* @param age 年龄
*/
//右键→source→Generate Constructor using Fields...
//直接生成有参构造方法
public Person02(String nickname,int gender,int age) {
this.nickname = nickname;
this.gender = gender;
this.age = age;
}
}
全参构造方法的调用
public class Person02Test {
@Test
public void test01() {
//初始化对象
Person02 person01 = new Person02("法外狂徒张三",1,20);
System.out.println("昵称是:" + person01.getNickname());
System.out.println("性别是:" + (person01.getGender()== 1 ? "男" : "女"));
System.out.println("年龄是:" + person01.getAge());
}
}
构造方法调用时机
public class Person02Test {
@Test
public void test01() {
//没有无参构造方法
//因为有了有参构造方法,所以jvm不会追加无参构造方法了
//因此无论任何情况下,一定要手写一个无参构造方法
//Person02 person01 = new Person02();
}
@Test
public void test02() {
Person02 person01 = new Person02("法外狂徒张三",1,20);
}
}
lombox工具简化书写
//自动生成setter @Setter
//自动生成getter @Getter
//自动生成无参构造方法 @NoArgsConstructor
//自动生成全参构造方法 @AllArgsConstructor
9、构造代码块
-
花括号包裹起来的我们称之为代码块或代码段
-
只有一对花括号包裹起来的我们称之为构造代码块或代码段
-
构造代码块与构造方法一样都是在类被实例化的过程中被调用的,并且构造代码块在构造方法之前先执行
-
类每次被实例化的过程中都会调用构造代码块
{
System.out.println("构造代码块");
}
10、静态代码块
使用static修饰的构造代码块我们称之为静态代码块
-
当类加载的时候执行静态代码块而且的静态代码块仅执行一次不可重复执行
-
而构造代码块是在类的实例化过程中执行的
-
执行优先级:静态代码块>构造代码块>构造方法
static{
System.out.println("静态代码块");
}
11、this调用
public class Person08 {
private final static int DefaultCapacity = 10;
private int[] capacity;
public Person08() {
//capacity = new int [DefaultCapacity];
//this在构造方法中调用本类的其他构造方法只能放在第一行,也就是说只能调用一个
//注意使用this调用构造方法的时候不允许相互调用造成死循环
this(new int[DefaultCapacity]);
}
public Person08(int[] capacity) {
super();
this.capacity = capacity;
}
public int[] getCapacity() {
return capacity;
}
public void setCapacity(int[] capacity) {
this.capacity = capacity;
}
}
12、包装类
-
jdk1.0提出了包装类的概念
-
包装类对象---->基本数据类型:拆箱
-
基本数据类型---->包装类对象:装箱
-
自动装拆箱jdk1.5
public class Wraper {
public static void main(String[] args) {
//八种数据类型对应的类
//Byte byte
//Short short
//Integer int
//Long long
//Double double
//Float float
//Character char
//Boolean boolean
Integer a = 10;
int b = a;
int c = 20;
Integer d = c;
Integer e = 127;
Integer f = 127;
Integer g = 128;
Integer h = 128;
int aa = 127;
int bb = 127;
int cc = 128;
int dd = 128;
//t,数据类型,相等
System.out.println(aa == bb);
//t,数据类型,相等
System.out.println(cc == dd);
//t,缓存在-128到127,没有超出缓存,不生成新的对象
System.out.println(e == f);
//f,因为缓存超过了-128到127之间,生成了新的对象
System.out.println(g == h);
System.out.println(dd == h);
}
}
十五、类的继承
1、继承 extends
写出一个父类
public class Person {
/**
* 昵称
*/
private String nickname;
/**
* 性别,1→男 0→女
* 默认值为0,女
*/
private int gender;
/**
* 年龄
*/
private int age;
public Person() {
super();
}
public Person(String nickname, int gender, int age) {
super();
this.nickname = nickname;
this.gender = gender;
this.age = age;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public int getGender() {
return gender;
}
public void setGender(int gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
写出一个继承父类的子类
//父类Person子类Male
public class Male extends Person{
}
子类实例化
public class PersonTest {
@Test
public void test01() {
Male male = new Male();
male.setNickname("张三");
male.setAge(20);
male.setGender(1);
System.out.println("昵称是:" + male.getNickname());
System.out.println("性别是:" + (male.getGender()== 1 ? "男" : "女"));
System.out.println("年龄是:" + male.getAge());
}
@Test
public void test02() {
Male male = new Male("张三",1,20);
Female female = new Female();
System.out.println("昵称是:" + male.getNickname());
System.out.println("性别是:" + (male.getGender()== 1 ? "男" : "女"));
System.out.println("年龄是:" + male.getAge());
}
}
2、继承后的实例化过程
父类
public class Person {
public Person() {
System.out.println("Person无参构造方法");
}
}
子类
//父类Person子类Male
public class Male extends Person {
public Male() {
System.out.println("Male无参构造方法");
}
}
实例化子类过程中先实例化父类
@Test
public void test01() {
//子类实例化过程中先实例化父类
Male male = new Male();
}
//输出结果 //Person无参构造方法 //Male无参构造方法
3、子父类关系
父类
public class Person {
public void eat() {
System.out.println("吃");
}
}
子类
//父类Person子类Male
public class Male extends Person {
public void work() {
System.out.println("work");
}
}
子类可以调用父类的方法,也可以拥有独有的方法
@Test
public void test01() {
Male male = new Male();
//子类可以调用父类的方法
male.eat();
//子类可以拥有独有的方法
male.work();
}
public Male(String nickname, int gender, int age) {
//引用父类
//super调用父类的构造方法时只能放在第一行
super(nickname, gender, age);
System.out.println("Male有参");
}
4、方法重写
当父类的方法满足不了子类的需求是,子类可以重写父类的方法
父类
public class Person {
public void eat() {
System.out.println("吃");
}
}
子类
//父类Person子类Male
public class Male extends Person {
//重写父类
/**
* 方法的重写
* 当父类的方法满足不了子类的需求是,子类可以重写父类的方法
*/
@Override
public void eat() {
System.out.println("吃核桃");
}
}
子类可以调用父类的方法,若方法已经重写,则调用的是重写之后的方法
@Test
public void test01() {
Male male = new Male();
//子类可以调用父类的方法,若方法已经重写,则调用的是重写之后的方法
male.eat();
//输出吃核桃
}
final修饰的方法不能重写
父类
public class Person {
public void eat() {
System.out.println("eat SCP-377绝对准确幸运饼干");
}
public final void drink() {
System.out.println("drink SCP-207超人可乐");
}
}
子类
public class Male extends Person {
public void eat() {
System.out.println("eat SCP-643巧克力糖");
}
//Cannot override the final method from Person
//final修饰的方法不能重写
//public void drink() {}
}
重写与重载
/**
* 方法重载与方法重写
* 在同一个类中,参数列表不同的同名方法我们称之为方法重载
* 父类的方法满足不了子类需求,子类 重写父类的方法我们称之为方法重写
* 方法重载在同一个类中
* 方法重写必须存在子父类继承关系
* @author 张世翔
* 上午11:31:43 2022年3月24日
* @version 1.0.0
*/
public class Inheritance {
public static void main(String[] args) {
Super s = new Sub();
Goo goo = new Goo();
goo.g(s);
//输出结果
//重载看类型
//调用的是super的obj
//g.(Super)..
//重写看类
//实例化指向的是子类
//Sub.f()
}
}
class Super {
public void f() {
System.out.println("Super.f()");
}
}
class Sub extends Super {
@Override
public void f() {
System.out.println("Sub.f()");
}
}
class Goo {
public void g(Super obj) {
System.out.println("g.(Super)..");
obj.f();
}
public void g(Sub obj) {
System.out.println("g.(Sub)..");
obj.f();
}
}
输出
g.(Super)..
Sub.f()
5、向上转型与向下转型
父类
public class Person {
public void eat() {
System.out.println("吃");
}
}
子类
//父类Person子类Male
public class Male extends Person {
@Override
public void eat() {
System.out.println("吃核桃");
}
public void work() {
System.out.println("work");
}
}
向上转型,随便转
@Test
public void test01() {
//父类对象的引用指向了子类对象实例化的的对象
//类型是父类,指向的是子类对象
//调用的是子类的方法
//向上转型,随便转
Person male = new Male();
//父类有的,子类重写过了,使用的是子类
male.eat(); //吃核桃
//父类没有的子类的方法,无法调用,因为栈内存是父类,没有指向
//male.work();
}
向下转型,强制转换
@Test
public void test02() {
//子类不能指向父类对象
//向下转型,强制转换
//父对象=new 子类();
//子对象=(子类)父对象;
//只能转换实例化对象,类不能改
Person person01 = new Male();
Male male = (Male) person01;
//实例化的是父类对象
//但是强制转换成了子类对象
//父类有的,子类重写过了,使用的是子类的方法
person01.eat(); //吃核桃
male.eat(); //吃核桃
male.work(); //work
}
十六、内部类与抽象类
1、内部类
调用内部类中的静态方法
public class Outer01 {
public static class Inner01{
public static void run() {
System.out.println("run");
}
}
}
直接调用
@Test
//直接调用静态内部类中的静态方法
public void outer01() {
Outer01.Inner01.run();
}
调用非静态内部类中的静态方法
public class Outer02 {
public class Inner01{
public static void run() {
System.out.println("run");
}
}
}
直接调用
@Test
//调用非静态内部类中的静态方法,可以直接调用
public void outer02() {
Outer02.Inner01.run();
}
调用非静态内部类中的非静态方法
public class Outer03 {
public class Inner01{
public void run() {
System.out.println("run");
}
}
}
先实例化
@Test
//调用非静态内部类中的非静态方法,需要先实例化
public void outer03() {
//Outer03.Inner01.run();
new Outer03().new Inner01().run();
}
调用非静态内部类中的非静态方法
public class Outer04 {
public class Inner01{
public void run() {
System.out.println("run");
}
}
public void innerRun() {
new Inner01().run();
}
}
在外部类写一个非静态方法调用内部类,然后在实例化该方法
@Test
//也可以通过在外部类写一个非静态方法调用内部类,然后在实例化该方法
public void outer04() {
//Outer03.Inner01.run();
new Outer04().innerRun();
}
2、局部内部类
1>
public class Outer05 {
// 局部内部类,静态
public static void innerRun() {
class Inner01 {
public static void run() {
System.out.println("run");
}
}
Inner01.run();
}
}
直接调用
@Test
public void outer05() {
Outer05.innerRun();
}
2>
public class Outer06 {
//局部内部类,静态
public static void innerRun() {
class Inner01{
public void run() {
System.out.println("run");
}
}
new Inner01().run();
}
}
直接调用
@Test
public void outer06() {
Outer06.innerRun();
}
3>
public class Outer07 {
//局部内部类,非静态
public void innerRun() {
class Inner01{
public void run() {
System.out.println("run");
}
}
new Inner01().run();
}
}
实例化
@Test
public void outer07() {
new Outer07().innerRun();
}
4>局部内部类匿名对象
public class Outer08 {
// 局部内部类
public void innerRun() {
class Inner01 {
}
new Inner01() {
public void run() {
System.out.println("run");
}
}.run();
}
}
实例化
@Test
public void outer08() {
new Outer08().innerRun();
}
3、匿名内部类
public class Outer09 {
public void innerRun() {
//Object()
new Object() {
public void run() {
System.out.println("run");
}
}.run();
}
}
4、抽象类
使用abstract修饰的类称之为抽象类
使用abstract修饰没有方法体的方法我们称之为抽象方法
具备抽象方法的类必须是抽象类
public abstract class Person {
//具备抽象方法的类必须是抽象类
public abstract void eat() ;
}
抽象类不一定要有抽象方法
public abstract class Person {
// 抽象类不一定要有抽象方法
public void eat() {
System.out.println("吃");
}
}
5、抽象类的继承
子类继承抽象类
抽象父类
public abstract class Person {
public abstract void eat() ;
}
继承-1>重写抽象父类中的抽象方法
public class Male extends Person {
public void eat() {
System.out.println("000");
}
}
继承-2>子类也是抽象类
public abstract class Female extends Person {
}
注意:使用final修饰的类不能被继承,不能同时使用abstract和final
类一
public final class Person {
public final void drink() {
System.out.println("drink SCP-207");
}
}
类二(此时无法继承)
//使用final修饰的类不能被继承
//public class Male extends Person {
public class Male {
public void eat() {
System.out.println("000");
}
}