Java面向对象编程——part2 封装继承多态
这部分是理解重点
IDEA快捷键
删除当前行ctrl+d;复制当前行ctrl+alt+向下箭头;补全代码alt + /;
添加或者取消注释ctrl+/;自动格式化ctrl+alt+L;运行快捷键alt +R;
自动生成构造器alt+insert;查看类的继承关系ctrl+H;ctrl+B跳转到某个类/方法的定义位置;
模板
maina模板等于public static void main(String[] args) {}
sout模板等于System.out.println();
fori模板等于for (int i = 0; i < ; i++) {}
包
作用:区分相同名字的类,管理类,控制访问范围
实际就是创建文件夹/目录保存类文件;
package com.hspedu; //com.hspedu表示包名,package关键字打包
//idea中右键新建package->com.xiaoming和com.use
package com.use; //声明当前Test类在com.use包(写在最上面)
import com.xiaoqiang.Dog; //在package下面,类定义上面
public class Test{
public static void main(String[] args) {
Dog d1 = new Dog; //调用xiaoqiang包里的Dog类
}
}
//常用的包
java.util.* //工具包,如java.util.Scanner
//引入包
import java.util.Scanner; //只引入 java.util包下面的一个类
import java.util.*; //导入包下的所有类
import java.util.Arrays;
//使用
Arrays.sort(arr);

包的命名:com.项目名.业务模块名
访问修饰符
用于控制方法(成员方法),属性(成员变量)和类的访问权限
只有public和默认能修饰类


面向对象的三大特征:封装、继承、多态
**封装**
把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,只有被授权的方法才能用;
作用:隐藏实现的细节,可以对数据进行验证;
封装的实现步骤
1、将属性私有化private;
2、提供一个公共(public)的set方法,用于对属性判断并赋值;
3、提供一个公共(public)的get方法,用于获取属性的值;
//封装案例
package com.encap;
public class encapsulation01 {
public static void main(String[] args) {
Person p1 = new Person();
p1.setName("jack");
p1.setAge(10);
p1.setSalary(3000);
System.out.println(p1.info());
Person p2 = new Person("smith",80,9000);
System.out.println(p2.info());
}
}
class Person{
public String name;
private int age; //私有化-main里不能直接赋值
private double salary;
public Person() {
}
public Person(String name, int age, double salary) {
setName(name);
setAge(age);
setSalary(salary);
}
//快捷键alt+insert的getter and setter
//用于对属性判断并赋值and获取属性的值
public String getName() {
return name;
}
public void setName(String name) {
if(name.length() >= 2 && name.length() <= 6){
this.name = name;
}else{
System.out.println("名字长度不符");
this.name = "noname";
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age >= 1 && age <= 120) {
this.age = age;
}else{
System.out.println("年龄需要在1-120");
this.age = 18; //给默认年龄
}
}
public double getSalary() {
//可以增加权限判断
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String info(){
return "信息为 name=" + name + " age=" +
age + " salary=" + salary;
}
}
**继承**
代码复用性——当多个类存在相同的属性和方法时,可以抽象出父类定义相同的属性和方法,子类通过extends来声明继承父类即可。

父类的私有属性和方法不能在子类直接访问,但父类可以提供一个public方法间接访问;
public class extends01 {
public static void main(String[] args) {
pupil p1 = new pupil();
//先调父类构造器student(),再调子类pupil()
p1.age = 10;
p1.name = "p1"; //score不能直接赋值,是私有的
p1.testing();
p1.setScore(99); //公共方法访问
p1.showInfo();
graduate g1 = new graduate();
g1.age = 23;
g1.name = "g1";
g1.testing();
g1.setScore(66);
g1.showInfo();
}
}
//父类,是graduate的父类
public class student {
//共有属性
public String name;
public int age;
private double score;
private int n;
//构造器
public student() {
}
//共有方法
public void setScore(double score) {
this.score = score;
}
public int getN() {
return n;
}
public void showInfo(){
System.out.println("学生名" + name +
" 年龄" + age + " 成绩" + score);
}
}
//子类
public class graduate extends student{
public void testing(){
System.out.println("大学生" + name
+ " 正在考大学数学");
}
}
子类必须调用父类的构造器,完成父类的初始化;
创建子类对象时,默认先去调父类的无参构造器,如果没有,需要在子类构造器中用super去指定父类构造器完成初始化,子类哪个构造器均可;(父类写的是有参构造器时,子类构造器需要写super(”tom”, 10);)
//假设父类有多个构造器
public sub(){
super(); //表示父类的无参构造器被调用(不写默认调用)
super("jack"); //表示调用父类一个参数的构造器
super("jack", 10); //表示调用父类两个参数的构造器
System.out.println("子类sub()构造器被调用");
}
super在使用时,必须放在构造器的第一行(super只能在构造器中使用)
super()和this()两种调用方法都只能放在构造器第一行(不共存)
所有类都是Object的子类,构造器的调用一直向上追溯到Object类
子类最多只能继承一个父类
继承的本质

public class extends02 {
public static void main(String[] args) {
son son = new son();
//按照查找关系来返回信息
System.out.println(son.name); //大头儿子
System.out.println(son.age); //报错private
System.out.println(son.getAge()); //39
System.out.println(son.hobby); //旅游
}
}
class grandpa{ //父类
String name = "大头爷爷";
String hobby = "旅游";
int age = 80;
}
class father extends grandpa{
String name = "大头爸爸";
private int age = 39;
public int getAge(){
return age;
}
}
class son extends father{
String name = "大头儿子";
}
查找关系(1)子类有该属性且可以访问,则返回信息;
(2)子类没有这个属性,就向上查找直到Object返回信息;
super关键字
用于访问父类的属性,方法和构造器
private私有的属性和方法访问不了,访问构造器只能放在第一行;
public void sum(){
System.out.println("子类的sum()方法");
//调用父类cal方法的几种方法
cal(); //首先找本类,没有则逐级向上找,找到但不能访问会报错
this.cal(); //同上
super.cal(); //直接从父类向上找
}

方法重写/覆盖
子类有一个方法和父类的某个方法的名称,返回类型和形参列表一样,就说子类sub的A方法重写了父类base的A方法;
返回类型一样,或父类的返回类型是子类返回类型的父类;
Object是String的父类(反过来写会报错)
子类方法不能缩小父类方法的访问权限public>protected>默认>private
**多态**
方法或对象具有多种形态,建立在封装和继承基础上;
方法的重写体现多态(不同对象调用到不同say方法)
多态的前提是:两个对象(类)存在继承关系
//对象的多态
//(1)一个对象的编译类型和运行类型可以不一致
//(2)编译类型在定义对象时就确定了不能改变,而运行类型可以改变
public class Test {
public static void main(String[] args) {
//编译类型Animal,运行类型Dog,可以父类的引用指向子类的对象
Animal a = new Dog();
Animal b = new Cat();
a.say(); // 汪汪
b.say(); // 喵喵
a = new Cat(); //运行类型可以改变(a是引用不是对象)
}
}
class Animal {
public void say() {}
public void eat() {}
}
class Dog extends Animal {
public void say() {
System.out.println("汪汪");
}
public void dogsay(){}
}
class Cat extends Animal {
public void say() {
System.out.println("喵喵");
}
}
多态的向上转型
父类的引用指向子类的对象
父类类型 引用名 = new 子类类型();
//编译类型Animal,运行类型Dog
Animal a = new Dog();
a.eat();
a.say();
//可以调用父类的所有成员(属性和方法)(遵循访问权限)
//不可以调用子类特有的成员(属性和方法)
//最终的运行结果按照子类的实现(运行类型)
多态的向下转型
子类类型 引用名 = (子类类型)父类引用;
只能强转父类的引用a,不能强转父类的对象;
Animal a = new Dog(); //向上转型
//编译类型Dog,运行类型Dog
Dog b = (Dog) a; //向下转型
b.dogsay(); //此时可以调用子类所有的成员(包括特有的)
//父类的引用必须指向当前当前目标类型的对象
Animal animal = new Cat();
Cat cat = (Cat) animal; //可以
Dog dog = (Dog) animal; //不可以,报错
属性没有重写,属性的值直接看编译类型
instanceof 比较操作符,判断对象的运类型是否为XX类型或XX类型的子类型;
public class Test {
public static void main(String[] args{
Base base = new Sub();
System.out.println(base.count); //编译类型,输出10
Base base1 = new Sub();
System.out.println(base1 instanceof Sub); //true
System.out.println(base1 instanceof Base); //true
}
}
class Base{
int count = 10;
}
class Sub extends Base{
int count = 20;
}
java的动态绑定机制
当调用对象方法时,该方法会和该对象的内存地址/运行类型绑定;
当调用对象属性时,没有动态绑定机制,哪里声明哪里使用;
public class dynamicBind {
public void main(String[] args) {
A a = new B(); //a的编译类型A,运行类型B
//先找子类的方法再向上找父类的sum方法,此时getI()调哪个?
//动态绑定机制:调方法会和运行类型绑定,getI()用子类的
System.out.println(a.sum()); //20+10
//属性不绑定,直接用父类的i
System.out.println(a.sum1()); //10+10
}
}
class A {
public int i = 10;
public int sum(){
return getI() + 10;
}
public int sum1(){
return i + 10;
}
public int getI(){
return i;
}
}
class B extends A{
public int i = 20;
public int getI(){
return i;
}
}
多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型;
//Person是父类,其子类是Student和Teacher
Person[] persons = new Person[5]; //创建父类引用数组
persons[0] = new Person("jack", 20);
persons[1] = new Student("tom", 18, 60);
persons[2] = new Student("amy", 22, 80);
persons[3] = new Teacher("smith", 35, 2000);
persons[4] = new Teacher("amy", 45, 4000);
//共有方法调用
for (int i = 0; i < persons.length; i++){
//动态访问机制,编译类型一直是Person
System.out.println(persons[i].say()); //访问每个类的say()方法
//特有方法调用
if(persons[i] instanceof Student){
Student stu = (Student)persons[i]; //向下转型
stu.study();
}else if(persons[i] instanceof Teather){
Teather tec = (Teather)persons[i]; //向下转型
tec.teach();
}else if(persons[i] instanceof Person){}
}
多态参数:方法定义的形参是父类,实参允许为子类
Object类
所有类的父类,其方法都可以用,几个常用方法如下:
//equals方法(只能由引用类型调用)
"hello".equals("abc"); //false
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println( str1 == str2); //false地址不同
System.out.println( str1.equals(str2)); //true值相同
/*题目:==和equals的对比
==可以判断基本类型值是否相等,引用类型的地址是否相等(是否同一对象)
equals只能由引用类型调用,默认是比较地址是否相等,
而子类往往会重写该方法,如Integer和String重写为判断值是否相等
*/
//Doctor类(name,age属性)重写equals方法
@Override
public boolean equals(Object obj){
if(this == obj){
return true;
}
if(!(obj instanceof Doctor)){
return false;
}
Doctor d = (Doctor)obj;
return this.age == d.age &&
(name == null ? d.name == null : name.equals(d.name));
}
去 JDK 的源码里看某个类 / 方法的实现 ctrl+b
//hashCode方法(返回对象的哈希码值)
//两个引用指向同一个对象,则哈希值肯定是一样的(基于地址得到)
//toString方法(返回对象的字符串表示)
//默认返回:全类名(包名+类名) + @ + 哈希值的十六进制
//重写方法toString()-可以用于打印对象/拼接对象alt+insert
//直接输出对象,默认等价加上.toString()
@Override
public String toString(){ //重写打印对象的属性
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", job='" + job + '\'' +
'}';
}
//finalize方法(当对象被回收前,系统自动调用该对象的finalize方法)
//对象没有引用时,垃圾回收器会回收/销毁对象
//子类可以重写这个方法-释放资源的操作alt+insert
断点调试Debug
断点调试时,是运行状态,按对象的运行类型来执行
F7跳入;F8跳过;shift+F8跳出;F9resume执行到下个断点
F8逐行执行代码,跳过方法内部;
F7可以追踪方法的源码;
退出方法写return;
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)