//1,良好的封装能够减少耦合
//2,类内部的结构可以自由修改
//3,可以队成员进行更精确的控制
//4,隐藏信息,实现细节
//先将属性选择性的隐藏,然后将需要给他人调用的内容,提供getter setter当作入口,可以在getter,setter进行设置操作,确保属性安全
public class Dog {
String dogName = "无名氏";//狗名称
private int age = 0;//年龄(0-15)
char sex = '女';//性别
int health = 60;//健康值(0-100)默认60
public void setAge(int age) {
if (age < 0 || age > 15) {
System.out.println("狗狗的年龄不能小于0也不能大于15");
return;
}
this.age = age;
}
public int getAge() {
return age;
}
//狗狗类转化成字符串的方法
public String toString() {
return "狗狗的自白:我的名字是:" + dogName +
",我今年" + age + "岁了,我是一个" + sex +
"孩子,我的健康度是" + health;
}
}
import java.util.*;
此时导入的是java.util包中所有的类
而不是
把整个java.util包导入
这两句话的区别就在于一个是只提到类,另一句把包下的包也提到了,但是导入*是只导入类,不导入包下的包的
public class Pet {
private int age = 0;
private char sex = '女';
private int heatlth = 60;
public int getAge() {
return age;
}
public void setAge(int age) {
if (age<0||age>15){
System.out.println("数据错误");
return;
}
this.age = age;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getHeatlth() {
return heatlth;
}
public void setHeatlth(int heatlth) {
this.heatlth = heatlth;
}
}
public class Cat extends Pet{
private String catName;
public String getCatName() {
return catName;
}
public void setCatName(String catName) {
this.catName = catName;
}
@Override
public String toString() {
return "Cat{" +
"catName='" + catName + '\'' +
"age="+getAge()+" , "+
"sex="+getSex()+" , "+
"health="+getHeatlth()+
'}';
}
}
使用继承的前提是会存在多个类,换句话说,如果你只需要一个类,那么就不需要使用继承,多个类使用继承的前提条件是这些类均存在一些相同的东西(属性,方法),为了使我们写的代码可以更好的复用(其实就是某种意义上的偷懒)我们会将这些相同的东西(属性,方法)进行提取,封装成一个超类(父类)让子类去继承他
public class Cat extends Pet{
private String catName = "无名氏";
public Cat(){
System.out.println("这是子类Cat无参构造器```");
}
public Cat(String catName,int age,char sex,int health){
//super在构造器当中 如果你需要调用父类的构造器
//那么你的super必须要写在这个子类构造器的代码的第一行位置
super(age, sex, health);
System.out.println("这是子类Cat有参构造器···");
this.catName = catName;
}
}
public class Pet {
private int age = 0;
private char sex = '女';
private int heatlth = 60;
public Pet(){
System.out.println("父类Pet无参构造器···");
}
//有参构造器
public Pet(int age,char sex,int health){
System.out.println("这是父类Pet有参构造器···");
if(age<0||age>15){
System.out.println("数据错误");
return;
}
this.age = age;
this.sex = sex;
this.heatlth = health;
}
}
public class Test {
public static void main(String[] args) {
Cat cat = new Cat("加菲猫",88,'男',200);
System.out.println(cat);
}
}
public class Cat extends Pet{
public void print(String str){
System.out.println("这是子类的print" );
}
public void superPrint(String str){
//这里一定要加super否则根据就近原则会调用当前类的print(String str)方法
super.print(str);
}
}
public class Pet {
public void print(String str){
System.out.println("这是父类的print");
}
}
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
cat.superPrint("shijinlong");
}
}
public class Cat extends Pet{
public String str1;
public void print(String str){
//调用子类的str1属性
System.out.print(str1);
//调用父类的str1属性
System.out.println(super.str1);
}
}
a,成员变量,效果为:将被修饰的属性从对象的属性,修改为类自身的属性,从而会变成无论这个类的什么对象,调用的属性均为类的属性,给一个赋值,所以的都会变化
public class Test {
public static void main(String[] args) {
C.iNum=10;
System.out.println("C的iNUm是:" + C.iNum);
C c1 = new C();
//c1.iNum=15;
System.out.println("c1的iNUm是:" + c1.iNum);
C c2 = new C();
System.out.println("c2的iNUm是:" + c2.iNum);
C.iNum=15;
C c3 = new C();
System.out.println("c3的iNUm是:" + c3.iNum);
System.out.println("c1的iNUm是:" + c1.iNum);
System.out.println("c2的iNUm是:" + c2.iNum);
}
}
public class C {
public static int iNum;
}
//运行结果
//C的iNUm是:10
//c1的iNUm是:10
//c2的iNUm是:10
//c3的iNUm是:15
//c1的iNUm是:15
//c2的iNUm是:15
b,成员方法
注意事项:静态方法中不能使用this和super
不能直接访问所属类的实例变量和实例方法
可直接访问类的静态变量和静态方法
可直接访问所属类的静态变量,静态方法,new对象去间接访问实例变量和实例方法
静态方法的方法体不能为空
如:
public class C {
static int iNUm;
int objINum;
static int iNum2;
public static void method1() {
//由于method1肯定是用C类去调用
//所以这里的自增的iNum2肯定是C类的iNum2
iNum2++;
/*
objINum不是static修饰,是属于对象的属性
我直接用C类去调用这个method1
那么我自增objINum我不知道是哪个对象被自增了
所以这里不能调用这个属性
objINum++;
不能调用非static修饰的方法也是相同的道理
首先非static修饰的方法是可以修改对象属性
你这样调用,不知道是哪个对象去调用了这个方法
所以不能调用非static修饰的方法————method2
*/
C c1 = new C();
c1.objINum = 10;
c1.method2();
//上面的调用————此时可以去调用的原因是因为这个已经不属于”直接调用“了
}
public void method2() {
System.out.println(objINum + iNUm);
}
}
c,代码块,效果为:当JVM加载这个类的时候,会将这个类中所有的static代码块执行一次,当然是只有类首次加载的时候,所以说static修饰的代码块,只会被执行一次,是在类首次加载的时候执行,首次加载包括但不限于初始化,调用类的属性等等
public class C {
static{
System.out.println("C类的静态代码块1");
}
static{
System.out.println("C类的静态代码块2");
}
static{
System.out.println("C类的静态代码块3");
}
}
public class Test {
public static void main(String[] args) {
C c1 = new C();
C c2 = new C();
C c3 = new C();
}
}
//运行结果
//C类的静态代码块1
//C类的静态代码块2
//C类的静态代码块3
静态代码块和构造器的加载顺序
由父及子,静态先行
public class A {
static int count;
static {
count++;
System.out.println("这是A的静态代码块");
}
public A(){
System.out.println("这是A的构造方法");
}
}
public class B extends A{
static {
System.out.println("这是B的静态代码块");
}
public B(){
super.count++;
System.out.println("这是B类的构造方法");
}
}
public class Test {
public static void main(String[] args) {
B b = new B();
System.out.println(A.count);
}
}
//运行结果
//这是A的静态代码块
//这是B的静态代码块
//这是A的构造方法
//这是B类的构造方法
//2
public class ZuoYe02 {
private int id = 0;
private String name = "";
public ZuoYe02(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj){
//第一步,先判空,如果传入的值是空就不用判断了
//因为调用这个肯定不是null,是null会抛空指针异常
if(obj==null){
return false;
}
//第2步判断内存地址是否相同
//如果相同,return true;
if(this==obj){
return true;
}
//第3步判断是否类型相同
if(obj instanceof ZuoYe02){
ZuoYe02 a = (ZuoYe02) obj;
//第4步判断userName和userid是否相同
if(this.getId()==a.getId()&&this.getName().equals(a.getName())){
return true;
}else{
return false;
}
}else{
return false;
}
}
}
public class TestZuo02 {
public static void main(String[] args) {
ZuoYe02 a = new ZuoYe02(60,"陆凯琪");
ZuoYe02 b = new ZuoYe02(60,"陆凯琪");
if(a.equals(b)){
System.out.println("对象一致");
}else{
System.out.println("对象不一致");
}
}
}
@Override
public boolean equals(Object obj){
//最基础的内容,可能会抛出空指针异常,直接比较用户获取用户名
//此时的equals方法是String类的equals方法
return getUserName().equals((User)obj.getUserName());
}
//idea提供了生成equals方法
@Override
public boolean equals(Object o){
//1判内存地址
if(this == 0) return true;
//2判断空 或者 3判断是否类型相同
if(o == null||getClass()!=o.getClass()) return false;
User user = (User)o;
//4判断userName是否相同
return Object.equals(sUerName,user.sUerName);
}
多态:同一个引用类型,使用不同的是实例执行不同操作
public class Animal {
public void move(){
System.out.println("动物在移动");
}
}
public class Bird extends Animal {
@Override
//重写父类中继承过来的方法
public void move() {
System.out.println("鸟儿在飞翔");
}
//子类特有的行为/动作
public void fly(){
System.out.println("Bird fly");
}
}
public class Cat extends Animal {
//重写父类中继承过来的方法
public void move() {
System.out.println("猫在走猫步");
}
//独有的方法,不是从父类继承过来的
public void catchMouse(){
System.out.println("猫在抓老鼠");
}
}
import com.kgc.day11.DuoTai.Animal;
import com.kgc.day11.DuoTai.Bird;
import com.kgc.day11.DuoTai.Cat;
/*
关于java语言当中的多态语法机制
1,Animal,Cat,Bird三个类之间的关系:
Cat继承Animal
Bird继承Animal
Cat和Bird之间没有任何继承关系
2,面向对象三大特征:封装,继承,多态
3,关于多态中涉及到的几个概念
*向上转型(upcasting)
-子类型--》父类型
又称为:自动类型转换
*向下转型(downcasting)
-父类型--》子类型
又被称为:强制类型转换【需要加强制类型转换符】
*需要记忆:
无论是向上转型还是向下转型,两种类型之间必须要有继承关系
*/
public class Test01 {
public static void main(String[] args) {
//以前编写的程序
Animal a1 = new Animal();
a1.move();
Cat c1 = new Cat();
c1.move();
c1.catchMouse();
Bird b1 = new Bird();
b1.move();
//使用多态语法机制
/*
1,Animal和Cat之间存在继承关系
2,Cat is a Animal
3,new Cat()创建的对象的类型是Cat,a2这个引用的数据类型是Animal,可见他们进行了类型转换
子类型转换成父类型,称为向上转型,自动类型转换
4,java中允许这种语法,父类型引用指向子类型对象
*/
Animal a2 = new Cat();
/*
1,程序永远都分为编译阶段和运行阶段
2,先分析编译阶段,再分析运行阶段
3,编译阶段编译器检查a2这个引用的数据类型为Animal,由于Animal。class字节码中
有move()方法,所以编译通过了,这个过程我们称为静态绑定,编译阶段绑定
只有静态绑定成功之后才有后续的运行,
4,在程序运行阶段,JVM堆内存中真实创建的对象是Cat对象,那么以下程序再运行阶段
一定会调用Cat对象的move()方法,此时发生了程序的动态绑定,运行阶段绑定
5,无论是Cat类有没有重写move方法,运行阶段一定调用的是Cat对象的move方法,因为底层
真实对象就是Cat对象
6,父类型引用指向子类型对象这种机制导致程序存在编译阶段绑定和运行阶段绑定
这种机制可以称为多态语法机制
*/
a2.move();//猫在走猫步
/*
a2.catchMouse();报错
因为编译阶段编译器检查到a2的类型是Animal类型
从Animal.class字节码文件当中查找catchMouse()
最终没有找到这个方法,导致静态绑定失败,编译失败,更别谈运行了
*/
/*
需求:假设想让以上的对象执行catchMouse方法怎么办
a2是无法直接调用的,因为a2的类型Animal。class中没有catchMouse方法
我们可以将a2强制类型转换成Cat类型
a2的类型是Animal(父类),转换成Cat类型(子类),被称为向下转型,强制类型转换
需要加强制类型转换符
什么时候需要向下转型呢:
当调用的方法是子类型中特有的,在父类中不存在,必须进行向下转型
*/
Cat c2 = (Cat) a2;
c2.catchMouse();
Animal a3 = new Bird();
/*
以下程序编译是没有问题的,因为编译器检查到a3的数据类型是Animal
Animal和Cat类型之间存在继承关系,并且Animal是父类型,Cat是子类型
父类型转换成子类型叫向下转型,语法合格,
Cat c3 = (Cat)a3;报错
程序编译虽然通过了,但是程序在运行阶段会出现异常,因为JVM堆内存中真实存在的对象
是Bird类型,Bird对象无法转换成Cat对象,因为两种你类型之间不存在任何继承关系
出现了著名的异常:
java.lang.ClassCastExcption
类型转换异常,这种异常总是在”向下转型的时候会发生
*/
/*
Cat c3 = (Cat)a3;报错
1,以上异常只有在强制类型转换的时候会发生,也就是说向下转型存在安全隐患
(编译过了但运行错了)
2,向上转型只要编译通过,运行肯定通过,像Animal a2 = new Cat();
3,想下转型编译通过,运行可能错误: Animal a3 = new Bird();Cat c3 = (Cat)a3;
4,怎么避免向下转型出现的classcastexception
使用instanceof运算符可以避免出现以上的异常
5,instanceof运算符怎么用:
语法格式:引用 (instanceof 数据类型名)
以上运算符的执行结果类型是布尔类型,结果可能是false||true
关于运算结果true/false
假设:(a instanceof Animal)
true:a这给引用指向的对象是一个Animal类型
false:a这个引用指向的对象不是一个Animal类型
6,在进行强制类型转换之前,建议使用instanceof运算符进行判断,避免classcastexception异常
*/
if(a3 instanceof Cat){//a3是一个Cat类型的对象就走这
Cat c3 = (Cat)a3;
c3.catchMouse();
}else if(a3 instanceof Bird){//a3是一个Bird类型的对象就走这
Bird b2 = (Bird)a3;
b2.fly();
}
}
}
public class Test02 {
public static void main(String[] args) {
//父类型引用指向子类型对象
//向上转型
Animal a1 = new Cat();
Animal a2 = new Bird();
a1.move();
a2.move();
//向下转型,【只有当访问子类对象当中特有的方法】
if (a1 instanceof Cat) {
Cat c1 = (Cat) a1;
c1.catchMouse();
}
if (a2 instanceof Bird) {
Bird b2 = (Bird) a2;
b2.fly();
}
}
}
在我们父类作为声明类型,子类作为实例化对象的时候,我们要注意有这么一个规则
编译看左,运行看右
在编写代码的时候,你要调用方法,那么我们需要看到的是该方法存在于父类中,你才能去调用,但是运行的时候,我们具体运行哪个方法是根据右侧实例化的结果来决定的,换言之,如果method1被子类重写,那么你声明的子类调用的方法就是他重写的方法
方法中,可以将形参设置成为父类类型,之后在调用时,直接使用子类实例去作为参数调用
“白马非马”
相当于多态,我们定义一个一个规则,所有的马都不能进城,那么我们只需要将参数写作抽象父类“吗”就可以,不需要具体写你是“白马”还是“黑马”
//抽象动物类
public abstract class Animal{
public abstract void toHosptial();
}
//动物类子类
public class Cat extends Animal{
@Override
public void toHospital(){
System.out.println("你成功带猫咪去医院了");
}
}
//动物类子类
public class Dog extends Animal{
@Override
public void toHospital(){
System.out.println("你成功带狗狗去医院了");
}
}
//主人类
public class Master{
//该方法的形参时父类
public void cure(Animal animal){
animal.toHospital();
}
}
//测试类
public class Test{
public static void mani(String[] args){
Master master = new Master();
Dog dog = new Dog();
Cat cat = newCat();
//调用cure的时候形参为父类,实参为子类
master.cure(dog);
master.cure(cat);
}
}
//作为返回值
public class PetStore{
//使用父类作为返回类型,实际返回实例化的对象为子类
public static Animal getAnimal(){
Scanner input = new Scanner(System.in);
System.out.println("请问你要什么: 1(猫) 2(狗)");
return 1 == input,nextInt() ? new Cat() : new Dog();
}
}
//测试方法
public static void main(String [] atgs){
while(true){
Master master = new Master();
Animal pet = Petstore.getAnimal();
//这里的照顾会根据你选择宠物类型而发生改变
master.cure(pet);
}
}
向上/下转型
向上转型其实问题并不大,因为如果你是一只“猫”,那我现在把你转化成一个动物肯定没有问题
向下转型就不太一样,你是一个动物我将你变成一只猫,如果你要是不是猫,你是一只狗,我把你变成猫就会出现转化异常,例如