面向对象进阶
面向对象进阶
一、递归
1.概念:方法自己调用自己,但是会在某一个时机进行第次返回。
注意:递归的行为,通常要放在递归的条件之后
public class Test {
public void fun(int i){
i --;
if(i>=0){
fun(i);
}
}
}
二、构造方法
1.什么是构造方法
用于初始化一个对象的方法
2.特点
- 构造方法名就是类名
- 没有返回值
- 创建对象时调用
案例:
package com.mine.demo01;
public class Person {
public Person(){
System.out.println("DFshmily.STUDIO");
}
}
package com.mine.demo01;
public class Main {
public static void main(String[] args) {
Person person = new Person();
new Person();
new Person();
new Person();
new Person();
new Person();
}
}
结果:
DFshmily.STUDIO
DFshmily.STUDIO
DFshmily.STUDIO
DFshmily.STUDIO
DFshmily.STUDIO
DFshmily.STUDIO
结论
每调用一次构造方法,就会创建一个新的对象
注意:
如果在一个类当中,没有显式的创建构造方法,则会由编译器提供一个无参构造。
3.构造方法的参数
构造方法同样可以有参数,构造方法的形参和实参与普通方法的别无二致
案例:
public class Person {
public Person(String name){
System.out.println(String.format("我%s出生了,哈哈哈哈~!!!",name));
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person("DF");
}
}
结果:
我DF出生了,哈哈哈哈~!!!
三、方法重载
概念:一个类当中,重复载入多个同名方法
1.方法重载的要素:
- 方法名要一致
- 参数要不一致:参数的数量、类型、位置
- 和返回值没有关系
- 对构造方法和普通方法都有效
案例:
package com.mine.demo02;
public class Food {
/**
* 构造方法与重载
*/
public Food(){
System.out.println("0参构造");
}
public Food(int f){
System.out.println("1参构造");
}
public Food(int i,double f){
System.out.println("2参构造");
}
public Food(double i,int f){
System.out.println("2参构造");
}
/**
* 方法的重载
*/
public void eat(){
System.out.println("0参方法");
}
public void eat(int i){
System.out.println("1参方法");
}
public void eat(int n,double i){
System.out.println("2参方法");
}
public void eat(double a,int i){
System.out.println("2参方法");
}
}
2.重载方法的调用
根据参数来进行调用
案例1:构造方法重载调用
package com.mine.demo02;
public class Main {
public static void main(String[] args) {
/**
* 构造方法的调用
*/
Food food = new Food();
Food food1 = new Food(1);
Food food2 = new Food(1,3.14);
Food food3 = new Food(3.14,1);
}
}
结果:
0参构造
1参构造
2参构造
2参构造
案例2:普通方法重载调用
package com.mine.demo02;
public class Main {
public static void main(String[] args) {
/**
* 方法的调用
*/
food.eat();
food.eat(1);
food.eat(1,3.14);
food.eat(3.14,1);
}
}
结果:
0参的方法
1参的方法
2参的方法
2参的方法
四、this关键字
1.概念:特指对象本身
案例:
package com.mine.demo01;
public class Person {
public void print(){
System.out.println(String.format("Person的this打印结果:%s",this));
}
}
package com.mine.demo01;
public class Main {
public static void main(String[] args) {
Person p = new Person();
p.print();
System.out.println(String.format("Person的对象p打印结果:%s",p));
}
}
结果:
Person的this打印结果:com.mine.demo01.Person@5cad8086
Person的对象p打印结果:com.mine.demo01.Person@5cad8086
2.使用案例:
语法:
- this.属性名
- this.方法名()
- this()
package com.mine.demo03;
public class Person {
private int id;//这个访问修饰符是为了保证该属性无法被类以外直接调用
private String name;
private int age;
public Person(){
}
public Person(int id){
this.id = id;
}
public Person(int id,String name){
this(id);
this.name =name;
}
public Person(int id,String name,int age){
this(id,name);
this.age = age;
}
}
3.注意的细节
- this调用构造,必须在构造当中
- this调用构造,必须在构造的第一行,且只能有一行
- 在静态当中无法使用this
五、包
1.概念:
- 包的本质是目录的划分
- 划分目录的目的是为了防止类与类之间的命名冲突
- 包的命名格式:
- 必须使用小写字母
- 必须是域名倒置+模块名
- ”.“不可用与开始和结束,因为”.“在包的命名当中是用于划分目录层级的
- 使用规则
- 一个类如果引用另一个类,被引用的类会存在两种情况:在同一个包中,不在同一个包
- 在同一个包下的类的引用,不需要引入包的概念(直接用就可以)
- 不在同一个包下,需要引用该类的时候,需要首先引入该类,或者是其所在包下所有的类
- 一个类如果引用另一个类,被引用的类会存在两种情况:在同一个包中,不在同一个包
2.案例1:引入一个具体的类
package com.mine.demo07.cl2;//注意其所在包的位置
public class Person {
}
package com.mine.demo07.cl1;
import com.mine.demo07.cl2.Person;//引入了一个对应的具体的类
public class Main {
public static void main(String[] args) {
Person p = new Person();
}
}
3.案例2:引入一个包
package com.mine.demo07.cl1;
import com.mine.demo07.cl2.*;
public class Main {
public static void main(String[] args) {
Person p = new Person();
Dog d = new Dog();
}
}
注意:批量引入,只针对当前包当中类,不包含子包中的类
4.案例3:引入不同包下的同名类
package com.mine.demo07.cl1;
import com.mine.demo07.cl2.Dog;
public class Main {
public static void main(String[] args) {
Dog d = new Dog();
//使用全局限定名
com.mine.demo07.cl2.ccl.Dog d2 = new com.mine.demo07.cl2.ccl.Dog();
}
}
其他规则
- 每一个类当中都会有一个package,用于标明该类的位置,其有且只能有一行,并必须位于类的第一行
- 在java中有一个包是不需要显式引入的:java.lang
- import必须在package和类的申明之间
- import不能引入两个同名类,否则报错
- 包是辅助限权控制的重要介质
六、封装
1.getter/setter
对实体类的属性进行封装,其是封装的一种代表形式
步骤:
- 将所有属性的访问修饰符改为私有
- 通过setter方法给属性赋值
- 语法格式:
- 方法名set开头
- 属性名首字母大写,其他不变,拼接在set之后
- 参数和要赋值的属性一致
- 可以没有返回值
- 语法格式:
- 通过getter方法从属性取值
- 语法格式
- 方法名get开头
- 属性名首字母大写,其他不变,拼接在get之后
- 参数不能有
- 返回值类型和属性类型保持一致
- 语法格式
案例:
package com.mine.demo04;
public class Person {
//属性私有
private int id;
private String name;
private int sex;
private int age;
public void setId(int id){
this.id = id;
}
public int getId(){
return id;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setSex(int sex){
this.sex = sex;
}
public int getSex(){
return sex;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
}
package com.mine.demo04;
public class Main {
public static void main(String[] args) {
Person p = new Person();
p.setId(1);
p.setName("df");
p.setSex(1);
p.setAge(18);
System.out.println(String.format("id:%s\tname:%s\tsex:%s\tage:%s",
p.getId(),p.getName(),p.getSex(),p.getAge()));
}
}
结果:
id:1 name:df sex:1 age:18
2.封装的优化
package com.mine.demo04;
public class Person {
//属性私有
private int id;
private String name;
private int sex = -1;
private int age = -1;
public Person(){
}
public Person(int id,String name,int sex,int age){
this.setId(id);
this.setName(name);
this.setSex(sex);
this.setAge(age);
}
public void setId(int id){
if(id<0){
System.out.println("id取值范围错误!");
}
this.id = id;
}
public int getId(){
return id;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setSex(int sex){
if(sex !=0&&sex !=1){
System.out.println("性别范围错误!");
return;
}
this.sex = sex;
}
public int getSex(){
return sex;
}
public void setAge(int age){
if(age > 120||age<0){
System.out.println("年龄范围取值错误!");
return;
}
this.age = age;
}
public int getAge(){
return age;
}
}
package com.mine.demo04;
public class Main {
public static void main(String[] args) {
Person p = new Person();
p.setId(1);
p.setName("df");
p.setSex(3);
p.setAge(180);
System.out.println(String.format("id:%s\tname:%s\tsex:%s\tage:%s",
p.getId(),p.getName(),p.getSex(),p.getAge()));
}
}
结果:
性别范围错误!
年龄范围取值错误!
id:1 name:df sex:-1 age:-1
3.链式调用
package com.mine.demo05;
public class Person {
//属性私有
private int id;
private String name;
private int sex = -1;
private int age = -1;
public Person(){
}
public Person(int id, String name, int sex, int age){
this.setId(id);
this.setName(name);
this.setSex(sex);
this.setAge(age);
}
public Person setId(int id){
this.id = id;
return this;
}
public int getId(){
return id;
}
public Person setName(String name){
this.name = name;
return this;
}
public String getName(){
return name;
}
public Person setSex(int sex){
this.sex = sex;
return this;
}
public int getSex(){
return sex;
}
public Person setAge(int age){
this.age = age;
return this;
}
public int getAge(){
return age;
}
}
package com.mine.demo05;
public class Main {
public static void main(String[] args) {
test(new Person().setId(1)
.setName("df")
.setSex(1)
.setAge(18));
}
public static void test(Person p){
System.out.println(String.format("id:%s\tname:%s\tsex:%s\tage:%s",
p.getId(),p.getName(),p.getSex(),p.getAge()));
}
}
结果:
id:1 name:df sex:1 age:18
七、继承
1.概念
- 现实生活中的继承:是赠予和获取的关系,是一方将说要的一切交给另一方
- 程序中的继承要满足一个前提:is a
- 父类是具有共性的,子类是具有特性的
- java是单根继承(即一个子类,只能有一个直接父类)
2.语法
public class ClassName extends ClassName{
//类的主体
}
好处:
在子类当中,可以获取父类里,那些可以被继承的属性和方法
package com.mine.demo01;
public class Animal {
public void breathe(){
System.out.println("动物在呼吸");
}
}
package com.mine.demo01;
public class Pet extends Animal{
protected String name;
protected int age;
protected int sex;
public void sleep(){
System.out.println("宠物在睡觉");
}
}
package com.mine.demo01;
public class Dog extends Pet{
}
package com.mine.demo01;
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.sleep();
dog.breathe();
}
}
3.继承当中的构造方法
注意:
- 子类构造必然要调用一个父类构造
- 默认调用的是父类的无参构造
- 如果没有无参构造,可以使用关键字super
- 在子类当中可以使用this调用其他构造,但是最后一定要有super调用父类构造
-
super的使用和this基本一致
- this调用的是当前对象
- super调用的是父类对象
package com.mine.demo02; public class Father { protected int age; protected String name; public Father(String name){ this.name = name; } public Father(String name,int age){ this(name); this.age = age; } }
package com.mine.demo02; public class Child extends Father{ public Child(String name){ super(name); } public Child(String name,int age){ this(name); super.age = age; } }
4.无法继承的元素
- 构造方法无法被继承
- 私有成员:子类对父类的私有成员不可见
- 默认修饰符,在不同包的情况下,同样不可见
5.方法的重写(覆盖)
- 方法重写的前提:
- 方法名相同
- 参数相同
- 返回值相同,或者子类的返回值类型是父类返回值类型的子类型(返回值类型大的永远在父类那)
注意:一般在工作当中,继承的重写,就是子类当中写个一模一样的就行
package com.mine.demo03;
public class Father {
public void zhaChouDouFu(){
System.out.println("原味臭豆腐");
}
}
package com.mine.demo03;
public class Son extends Father {
@Override
public void zhaChouDouFu() {
System.out.println("香草味臭豆腐");
}
}
package com.mine.demo03;
public class Main {
public static void main(String[] args) {
Son son = new Son();
son.zhaChouDouFu();
}
}
结果:
香草味臭豆腐
注解:@Override
用来检查当前方法是否是重写的方法
6.一个常见的面试题
重写和重载的区别?
八、多态
同一个事物,在不同的情况下,表现出来的状态不一致
语法:父类申明指向子类对象
package com.mine.demo04;
public class Pet {
public void eat(){
System.out.println("宠物在吃东西");
}
}
package com.mine.demo04;
public class Dog extends Pet{
public void eat(){
System.out.println("骨头");
}
}
package com.mine.demo04;
public class Cat extends Pet{
public void eat(){
System.out.println("小鱼干");
}
}
package com.mine.demo04;
public class Main {
public static void main(String[] args) {
//父类申明指向子类对象
Pet dog = new Dog();
dog.eat();
Pet cat = new Cat();
cat.eat();
}
}
结果:
骨头
小鱼干
注意:
- 父类和子类之间,必须存在继承关系,但不一定是直接继承关系。即父类可以是爸爸,也可以是爷爷......
- 父类引用只可以调用父类的属性和方法,不可以调用子类的属性和方法。父类是一个标准!
1.装箱
父类申明指向子类对象
2.拆箱
把父类型向子类型转换
package com.mine.demo04;
public class Pet {
public void eat(){
System.out.println("宠物在吃东西");
}
}
package com.mine.demo04;
public class Dog extends Pet{
public void eat(){
System.out.println("骨头");
}
public void callWa(){
System.out.println("汪汪汪~");
}
}
package com.mine.demo04;
public class Cat extends Pet{
public void eat(){
System.out.println("小鱼干");
}
public void callMiao(){
System.out.println("喵喵喵~");
}
}
package com.mine.demo04;
public class Main {
public static void main(String[] args) {
//父类申明指向子类对象
Pet dog = new Dog();
dog.eat();
Pet cat = new Cat();
cat.eat();
//拆箱
Dog dog1 = (Dog) dog;
Cat cat1 = (Cat) cat;
//调用子类的属性和方法
dog1.callWa();
cat1.callMiao();
}
}
结果:
骨头
小鱼干
汪汪汪~
喵喵喵~
3.关键字:instanceof
用于判断某个对象是否是某个类型
案例:
import com.mine.demo01.Cat;
import com.mine.demo01.Dog;
import com.mine.demo01.Pet;
public class Main {
public static void main(String[] args) {
Pet d = new Dog();
Pet c = new Cat();
Pet p = new Pet();
//匹配类型
System.out.println(d instanceof Dog);
System.out.println(d instanceof Pet);
System.out.println(d instanceof Cat);
System.out.println(p instanceof Pet);
System.out.println(p instanceof Dog);
System.out.println(p instanceof Cat);
}
}
结果:
true
true
false
true
false
false
结论:判断类型是否匹配,主要观察的是实现类(实例)
4.优化拆箱
package com.mine.demo04;
public class Main {
public static void main(String[] args) {
//父类申明指向子类对象
Pet dog = new Dog();
dog.eat();
Pet cat = new Cat();
cat.eat();
Pet p = new Pet();
//拆箱
if(dog instanceof Dog){
Dog dog1 = (Dog) dog;
dog1.callWa();
}
if(cat instanceof Cat){
Cat cat1 = (Cat) cat;
cat1.callMiao();
}
//调用子类的属性和方法
}
}
九、Object类
所有类的超类,在整个Java体系中,都要直接或者间接的继承Object类
当一个类没有显式的继承某个类时,其将默认继承Object类
1.获取地址:hashCode
package com.mine.demo04;
public class Main {
public static void main(String[] args) {
Person p = new Person();
System.out.println(p.hashCode());
}
}
结果:
1163157884
2.打印对象:toString
Object的toString方法
如果要打印对象,会默认调用该方法。打印内容,为该方法返回的字符串
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
重写
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}
一个有价值的数据格式:json
- 对象:
- 集合或者数组:[{},{},{}]
[{"id":1,"name":"ybb","dogs":[{"name":"cp"},{"name":"bd"}]},
{"id":2,"name":"lzl","dogs":[{"name":"jp"},{"name":"xb"}]}]
3.比较两个对象是否相等,重写equals
package com.mine.demo05;
public class Person {
private int id;
private String name;
public void setId(int id){
this.id = id;
}
public int getId(){
return id;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public boolean equals(Object obj){
//1.比地址
if(this == obj){
return true;
}
//2.转类型
if(obj instanceof Person){
/*
//拿this p1和这个p2对象的属性值进行比较,
如果主观上所有属性值都相同,那么结果为true
有一个属性不同,结果为false
*/
Person p1 = this;
Person p2 = (Person) obj;
if(p1.id != p2.id){
return false;
}
if(p1.name!=null&&!p1.name.equals(p2.name)){
return false;
}
if(p2.name!=null&&!p2.name.equals(p1.name)){
return false;
}
}else {
//类型不同,直接不需要比
return false;
}
return true;
}
}
结论:提供一个在何种情况下才算是相等的规则,其属于一种约定。
作业2:
添加一个新的功能
删除学生信息
1.检查是否存在
2.删除后要排序
十、访问修饰符
- public(常用)
- private(常用)
- 默认
- protected
1.public:(共有的)
任何地方都可以访问
2.private:(私有的)
只能在本类当中
3.默认的
-
当前类当中
-
同一个包内可以访问
4.protected:受保护的
- 当前类当中
- 同一个包内可以访问
- 如果不在同一个包当中,想要访问有一个例外,即在其子类当中
案例:
package com.mine.demo03;
public class Test {
public int i;
protected int j;
int k;
private int h;
}
package com.mine.demo03.cl;
import com.mine.demo03.Test;
public class Test2 extends Test {
public void fun(){
super.j = 100;
}
}
十一、类图
注意:
+代表public
-代表private
变量名:变量类型
方法名(参数名:参数类型......):返回值类型
作业:
改造学员成绩管理系统:
- 将所有的类当中,属性全部隐藏,提供getter/setter方法,及其构造方法
- 增加一个功能,修改学生成绩
- 输入学生学号后,需要检查该学号是否存在
- 修改学生成绩后,重新排序