面向对象-中级篇
作者:互联网
包:
- 什么是包? 可以把它理解为一个文件夹
- 包的作用? 区分相同名称的类,方便管理类,控制访问范围
- 如何创建包?和创建文件夹一样
- 如何引入包?语法:import 包 , 如:import java.util.*
- 如果有两个一样的类名,就可以提供地址来区分是哪个包下的
-
包的命名规范:
- 只能包含数字,字母,下划线,小圆点
- 不能以数字开头,不能用关键字,保留字
- 正规格式:com.公司名.项目名.业务名
- com.TX.crud.user
-
常用的包:
- java.lane.* 是java的基础包,默认导入不需要引入
- java.util.* java提供的工具包
- java.net.* 网络开发包
- java.awt.* java页面开发包
-
细节:
- package(打包) - 作用:生明当前类是属于哪个包
- import(导入) - 作用:导入包
访问修饰符:
- 什么是访问修饰符? 用于控制方法和属性的访问权限
- 访问修饰符分为:4种访问修饰
- public - 公共的
- protected - 受保护的
- private - 私有的
- 默认的 - 就是什么都不写
-
细节:
- 修饰符,可以修饰属性和方法以及类
- 只有默认和public才能修饰类
- 属性和成员属性的访问完成一样
-
//细节2:只有默认和public才能修饰类 public class Sds { public int i = 1; //公共 protected int n = 2;//受保护的 int o = 3;//默认的 private int p = 4;//私有的 public void show(){ //方法和属性的访问修饰符是一样的 System.out.println("我是方法show"); } } //细节2:只有默认和public才能修饰类 class King{ }
封装:
-
什么是封装? 将类在的某些信息隐藏在类的内部,不允许外部直接访问,而是通过类提供的方法来对隐藏的数据进行访问和操作
-
封装的好处? 1.只能规定的方法访问和操作数据 ,2.可以对数据进行验证,保证安全合理,3.隐藏实现细节
-
封装的实现步骤:
-
将属性私有化private
-
提供一个setXxx(参数列表)方法,对属性进行判断和赋值
-
提供一个getXxx()方法,获取属性值
-
-
public class Test{ private String name; private String sex = "男"; private int age = 18; //获取属性值 public String getName() { return name; } public void setName(String name) { //进行判断和赋值 if(name.length() > 1 && name.length() < 5){ this.name = name; }else { System.out.println("你名称太长了"); } } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } } main{ Test.setName("jack,彭于晏,吴彦祖");//肯定是错的,因为条件判断最大是5 }
-
构造器和Set()方法结合:
-
如果使用构造器来给属性赋值,那么Set方法的判断就会失效,直接在构造器中调用Set方法就可以经解决
-
-
public Sds2(String name, String sex, int age) { //像使用构造器,就可以直接跳过Set()的判断了 // this.name = name; // this.sex = sex; //调用本类的方法就好set方法就好了 setName(name); setSex(sex); }
继承:
- 什么是继承? 符合is a条件的类可以通过继承来解决代码的复用,如动物类和小狗类,那么小狗就可以继承动物类,因为狗有动物的行为
- 为什么需要继承?减少代码的复用
-
继承示意图:
-
继承的基本语法:
-
class 子类 extends 父类{ //继承父类,知道要有父类的属性和方法 }
-
-
细节:
-
子类继承了父类,非私有的属性和方法可以直接在子类访问,但私有属性不能直接访问(需要通过父类的公共方法来访问)
-
子类必须调用父类的构造器,完成父类的初始化(理解:当去继承父类如果没调用构造器初始化父类,那么继承的是什么?)
- (简单理解:父亲都没有,哪来的你?)
- 创建子类对象,不管使用子类哪个构造器,默认会去调用父类的无参构造器,如果父类没有无参构造器,则需要在子类使用super来指定父类构造器,完成父类初始化
- 指定父类构造器方法:super(参数列表)
- super()使用时必须放在构造器第一行(super()只能在构造器中使用),因此super和this都是需要放在第一行的,所以不能同时在一个构造器使用
- java所有类都是Object类的子类,所以父类可以一直追溯到Object
- java是单继承机制,只能继承一个父类
- 父类和子类必须满足is-逻辑,如你是飞机类去继承汽车类,这不就扯蛋了
-
public static void main(String[] args) { B b= new B(); } class A{ A(){ System.out.println("我是A的无参构造器"); } A(String mame) { System.out.println("我是A的有参构造器"); } } class B extends A{//只能继承一个父类 B(){ //super();默认调用父类的无参构造器 //super("jack");指定父类的构造器 System.out.println("我是b的无参构造器"); } }
-
-
继承的本质:
- 本质:先去加载父类,在加载子类
- 问题:为什么创建的CarSon指向的堆中有父类的属性?
- 细节中说到:因为继承了父类就会获取子类中非私有的属性和方法
-
-
Super关键字:
-
什么是Super关键字? 可以用于直接访问父类的构造器、方法、属性
- 理解:本类不访问直接访问父类
-
class Car{ String name = "汽车"; String affect = "出行"; } class CarSon extends Car{ String name = "跑车"; String CarName = "Lamborghini"; public void jk(){ System.out.println("CarSon类jk方法"); } public void show(){ System.out.println(affect);//如果没指定调用的是父类的还是子类的,就从子类开始向上找 System.out.println(name);//输出跑车,这个也等价于this.name System.out.println(super.name);//输出汽车 } }
-
细节:
-
如不使用super,就从本类向父类查找成员,如果有重名就遵循就近原则,本类没有才找父类
-
找成员naem,如子类没有找父类,父类没有在找爷爷类。A->B->C,当然遵守访问权限原则
-
方法的重写/覆盖:
-
什么是方法的重写? 就是子类的某个方法和父类的某个方法,方法名称、返回类型、参数列表一样
-
细节:
-
子类的形参列表,方法名称,要和父类形参列表,方法名称完全一样
-
子类的返回类型要和父类一致,或保持父子关系,如父类是Object返回类型,子类是String返回类型
-
子类不能缩小方法的访问权限但是可以放大 public > protected > 默认 > private
-
class Car{ public void show(){ System.out.println("我是show方法"); } public Object show2(){ return 1.1; } private void show3(){ System.out.println("我是show3方法"); } } class CarSon extends Car{ //子类的形参列表,方法名称,要和父类形参列表,方法名称完全一样 public void show(){ System.out.println("我是子类,我重写了show方法"); } //子类的返回类型要和父类一致,或保持父子关系,如父类是Object子类是String public String show2(){ return "1.1"; } //子类不能缩小方法的访问权限但是可以放大 public > protected > 默认 > private public void show3(){ System.out.println("我是子类,我重写了show3方法"); } }
-
多态:
- 什么是多态? 多种形态,多态是建立在继承和封装的基层上
- 理解:完成某个行为的时候,不同的对象去完成会产生不同的状态
- 例子:方法的重写和重载就是多态的一种体现 -> 方法的多态
- 如父类引用=子类对象( 如:Animal animal = new Dog() ) - > 对象的多态
-
对象的多态:
- Animal animal = new Animal( ) - -> 左边是编译类型 - 右边是运行类型
- 一个对象的编译类型和运行类型可以不一致,当然只两者要保持继承关系
- 编译类型在确定对象后是不能改变的,运行类型可以改变
- 下面案例中就使用对象的多态完成了动物的喂食问题
- 小狗有父类,食物有父类,通过小狗和食物的父类编译类型去引用它的子类对象完成操作
-
public class Test { public static void main(String[] args) { person person = new person("汤姆"); Dog2 dog2 = new Dog2("小白"); Bone2 bone2 = new Bone2("骨头"); person.show4(dog2,bone2); //解决:用到多态对象就完美的解决该问题喂食方法的重复问题 Cat2 cat2 = new Cat2("小花"); Bone2 bone3 = new Bone2("骨头"); person.show5(cat2,bone3); } } //目的:让人去给动物喂食物 class Animal2{ private String name; public Animal2(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } class Fish2{ private String name; public Fish2(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } class Dog2 extends Animal2{ public Dog2(String name) { super(name); } } class Cat2 extends Animal2{ public Cat2(String name) { super(name); } } class Bone2 extends Fish2{ public Bone2(String name) { super(name); } } class person{ public person(String name) { this.name = name; } private String name;//主人名称 //问题:如果我有一百种小动物,那我的喂食方法也需要写一百个吗 public void show4(Dog2 dog2,Bone2 bone2){ //喂食方法 System.out.println("主人"+name+"给"+dog2.getName()+"吃"+bone2.getName()); } //解决:用到多态对象就完美的解决该问题 public void show5(Animal2 animal2,Fish2 fish2){ //喂食方法 System.out.println("主人"+name+"给"+animal2.getName()+"吃"+fish2.getName()); } }
-
多态向上转型:
- 什么是向上转型? 子类是运行类型,父类是编译类型
- 细节:
- 不能调用本身的特有成员,可以调用父类的所有成员(遵守访问权限)
- 为什么不能调用本身特有成员? 因为在编译阶段,能调用哪些成员,是由编译类型决定(说白了就是加载编译类型的成员变量)
- 因为最终的的运行看子类的具体表现
- 因为最终的的运行看子类的具体表现什么意思? - 调用成员时还是按子类向父类的向上查找
- 不能调用本身的特有成员,可以调用父类的所有成员(遵守访问权限)
-
public class Test { public static void main(String[] args) { //普通继承 - 可以调用本身特有的成员,和父类中所有的成员(遵守访问权限) Dog2 dog2 = new Dog2(); dog2.eat();//吃骨头 dog2.action(); dog2.show(); //多态向上转型 - 不能调用本身的特有成员,可以调用父类的所有成员(遵守访问权限) //为什么不能调用本身特有成员? 因为在编译阶段,能调用哪些成员,是由编译类型决定(说白了就是加载编译类型的成员变量) //Animal2是编译类型,Dog2是运行类型 //为什么animal2.eat();输出吃骨头不是吃东西? - 因为最终的的运行看子类的具体表现 //因为最终的的运行看子类的具体表现什么意思? - 调用成员时还是按子类向父类的向上查找 Animal2 animal2 = new Dog2(); animal2.eat();//吃骨头 } } class Animal2{ public void eat(){ System.out.println("吃东西"); } public void action(){ System.out.println("撒娇"); } } class Dog2 extends Animal2{ public void eat(){ System.out.println("吃骨头"); } public void show(){ System.out.println("展示才艺"); } }
-
多态向下转型:
-
什么是向下转型? 把父类的的引用强转,为编译类型是子类的对象( 如:Dog dog = (Dog)animal )
-
细节:
-
只能强转父类的引用,终于不是父类的对象
-
强转的父类引用必须是和当前对象有关联的
-
向下转型后,可以调用子类中所有的成员
-
-
Animal2 animal2 = new Dog2(); animal2.eat();//吃骨头 //多态的向下转型 - 强转需要保持关联性 //当初是Dog2向上转型为父类的animal2,那么向下转型也要保持这个关系 Dog2 dog3 = (Dog2) animal2; og3.show();
-
-
属性没有重写之说:
- 属性的值看编译类型 -
-
public class Test { public static void main(String[] args) { A a = new B(); System.out.println(a.name);//MIUI B b = new B(); System.out.println(b.name);//Huawei } } class A{ public String name = "MIUI"; } class B extends A{ public String name = "Huawei"; }
-
instanceOf比较操作符:
-
用与判断对象的运行类型是否为XX类型或XX类型的子类型
-
public class Test { public static void main(String[] args) { A a = new A(); System.out.println(a instanceof A);//真 A a2 = new B(); B b = new B(); System.out.println(b instanceof A);//真 } } class A{ public String name = "MIUI"; } class B extends A{ public String name = "Huawei"; }
-
-
java的动态绑定机制(重点):
- 什么是动态绑定机制?
- 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
- 当调用对象属性时,没有动态绑定机制,哪里声明哪里使用
- 什么是动态绑定机制?
-
public class Test { public static void main(String[] args) { A a = new B(); //没有把B类中的sum()方法注销时的输出结果 System.out.println(a.sum());//220 System.out.println(a.sum1());//210 //把B类中的sum()方法注销后输出的结果 //为什么是210? 因为地态绑定机制的原因,调用到getl()时就回到运行类型的调用,没有该方法在往上寻找 System.out.println(a.sum());//210 System.out.println(a.sum1());//210 } } class A{ public int a = 100; public int sum(){ return getl() + 10; } public int sum1(){ return a + 10; } public int getl(){ return a; } } class B extends A{ public int a = 200; // public int sum(){ // return a + 20; // } public int sum1(){ return a + 10; } public int getl(){ return a; } }
-
多态数组:
- 什么是多态数组? 在一个数组中可以存放不同的子类
- 案例:
-
public class Test { public static void main(String[] args) { Person[] person = new Person[5]; person[0] = new Student("小白",18,"学生"); person[1] = new Teacher("老王",32,"老师"); for (int i = 0; i < person.length; i++) { //show方法是重写的 - 但运行结果是根据运行类型 if(person[i] != null){ String show = person[i].show(); System.out.println(show); //向下转型,调用特有的成员 if(person[i] instanceof Student){ System.out.println(((Student)person[i]).study()); } if(person[i] instanceof Teacher){ System.out.println(((Teacher)person[i]).teach()); } } } } } class Person{ public Person(String name, int age) { this.name = name; this.age = age; } private String name; private int age; public String show(){ return name+"-"+age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } class Student extends Person{ public Student(String name, int age, String job) { super(name, age); this.job = job; } private String job; public String show(){ return super.show()+"-"+job; } public String study(){ return super.getName()+"在收听java"; } } class Teacher extends Person{ public Teacher(String name, int age, String job) { super(name, age); this.job = job; } private String job; public String show(){ return super.show()+"-"+job; } public String teach(){ return super.getName()+"给学生将java"; } }
Object顶级父类下的常见方法:
-
equals方法:
-
equlas:是Object类中的方法,默认(没被重写时)是判断引用类型的地址是否相等,子类往往重写该方法用与判断内容是否相等(如String和Integer)
-
一般 equals 和 == 会进行一个比较(面试):
-
== :是一个比较运算符
-
== :即可以判断该基本数据类型,又可以判引用类型
-
==:判断基本数据类型判断该的值,判断的是引用类型判断该的是地址
-
-
重写equals方法:
-
public class Test { public static void main(String[] args) { Person person1 = new Person("小白",18,'男'); Person person = new Person("小白",18,'男'); // System.out.println(person1.equals(person));//false(没有重写equals默认比较地址) System.out.println(person1.equals(person));//true(重写equals后) } } class Person{ public Person(String name, int age, char sex) { this.name = name; this.age = age; this.sex = sex; } String name; int age; char sex; public boolean equals(Object obj) { //this代表的就是obj(自己调用我自己) if(this == obj){ return true; } if(obj instanceof Person){ Person p = (Person)obj; return this.name == p.name && this.age == p.age && this.sex == p.sex; } return false; } }
-
-
hashCode方法:
- hashCode返回的是哈希码值,支持该方法是为了提高哈希表
- 哈希值主要根据地址类(但哈希值不完全等价于地址,因为Object的hashCode方法会根据不同的对象返回不同的整数)
- 两给引用,指向的是同一个对象,则哈希值肯定是一样,反知
-
public class Test { public static void main(String[] args) { Person person1 = new Person("小白"); Person person2 = new Person("小红"); System.out.println(person1.hashCode());//356573597 System.out.println(person2.hashCode());//1735600054 } } class Person{ public Person(String name) { this.name = name; } String name;
- toString方法:
- 默认返回:全类名+@+哈希值的十六进制
- 一般子类重写用来返回对象的属性信息,打印对象或拼接对象时,都会该方法会自动调用
-
public class Test { public static void main(String[] args) { Person person1 = new Person("小白","打游戏"); // System.out.println(person1.toString());//默认输出 System.out.println(person1);//重写输出:Person{name='小白', hobby='打游戏'} } } class Person{ public Person(String name, String hobby) { this.name = name; this.hobby = hobby; } String name; String hobby; @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", hobby='" + hobby + '\'' + '}'; } }
-
finalize方法:
-
finalize方法是做什么的? 当对象被回收时,系统自动调用该方法(注意不是该方法用来回收对象,而是在被回收前做一些操作)
-
什么时候被回收?当对象没有引用指向它时,jvm认为该对象就是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁前会调用finalize方法
-
垃圾回收机制?是由GC算法来决定(也就是说对象没引用的时候就马上销毁,而是由GC算法决定的),但是可以通过System.gc();来主动触发垃圾回收机制
-
public class Test { public static void main(String[] args) { BaoMa baoMa = new BaoMa("宝马"); baoMa = null; System.gc();//主动调用垃圾回收机制 System.out.println("程序退出"); } } class BaoMa{ public BaoMa(String name) { this.name = name; } String name; @Override protected void finalize() throws Throwable { System.out.println("销毁汽车"+name); System.out.println("释放资源"); } }
-
断点调试:
- 什么是断点调试? 通过一行一行的去执行代码,查看代码的执行
- 类加载的断点调试演示:
-
标签:中级,String,子类,class,面向对象,父类,public,name 来源: https://www.cnblogs.com/Mr-shne/p/16533055.html