1、访问修饰符
使用注意事项:
1、修饰符可以用来修饰类中的属性、成员方法及类。
2、只有默认和public才可以修饰类。
3、成员方法的访问规则和属性完全一样。
2、封装:
1、定义:
就是将抽象出来的数据、方法封装在一起,数据被保护在内部,程序的其它部分只能通过被授权的操作,才能对数据进行操作。
封装就是使用 private 修饰符保护数据,使用set、get方法修改及获取数据。
2、封装的意义:
隐藏实现细节,别人只需要调用即可,不关心是如何实现的。
可以对数据进行验证,将不合理的数据排除,保证数据的安全合理。
public class EncapSulation {
public static void main(String[] args) {
Person person = new Person("lisa",100, 20000.0);
double res = person.getSalary();
System.out.println(res);
}
}
class Person {
public String name;
private int age;
private double salary;
// 封装和构造器:
// 可以将set方法写在构造器里面,这样就不会担心封装被构造器破坏
public Person(String name, int age, double salary) {
// this.name = name;
// this.age = age;
// this.salary = salary;
this.setName(name);
this.setAge(age);
this.setSalary(salary);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
// 可以在set方法中使用判断语句来控制数据的合理性
public void setAge(int age) {
if (age > 1 && age <= 120) {
this.age = age;
} else {
System.out.println("年龄设置不合理");
this.age = 30;
}
}
public double getSalary() {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入密码");
int password = scanner.nextInt();
if (password == 123) {
return salary;
} else {
System.out.println("密码输入错误");
return 0;
}
}
public void setSalary(double salary) {
this.salary = salary;
}
public void message() {
System.out.println(this.name + " " + this.age + " " + this.salary);
}
}
3、继承:
1、定义:
当多个类存在相同的属性和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要使用 extends 来声明继承父类即可。
简而言之:就是将多个类共有的属性方法拿出来,让其它需要使用的类来继承它(使用它)。
2、意义:
解决代码的复用性。
3、代码:
父类
public class Student {
public String name;
public int age;
private double grade;
public void setGrade(double grade) {
this.grade = grade;
}
public void ShowGrade() {
System.out.println("姓名为:" + name + "年龄为" + age + "成绩为" + grade);
}
}
子类
public class Graduate extends Student{
public void test() {
System.out.println("大学生" + name + "正在考数学");
}
}
3、细节
a、子类继承了父类所有的属性和方法,非私有的属性和方法可以在子类直接访问,
但是私有的属性和方法不能再子类直接访问,要通过父类提供的公共方法区访问。
public class Student {
public int score1 = 100;
public int score2 = 80;
private int score3 = 90;
// 父亲提供一个public方法,返回score3
public int getScore3() {
return score3;
}
public void test1() {
System.out.println("学生正在考试数学");
}
public void test2() {
System.out.println("学生正在考试英语");
}
private void test3() {
System.out.println("学生正在考试语文");
}
// 父亲提供一个public方法,调用test03
public void callTest() {
test3();
}
}
b、子类必须调用父类的构造器,完成父类的初始化。
c、当创建子类对象时,不管使用子类哪个构造器,默认情况都会去调用父类的无参构造器。
如果父类没有提供无参构造器,则必须在子类的构造器中用supre去指定使用父类的哪个构造器完成对父类的初始化工作,否则编译 不会通过。
d、如果希望指定去调用父类的某个构造器,则需要显示的调用一下:super(参数列表);
e、super在使用时,必须放在构造器第一行(super只能在构造器中使用)。
f、super()和this()都只能放在构造器第一行,因此两个方法不能共存在一个构造器中。
g、Java中所有类都是object类的子类。
h、父类构造器的调用不限于直接父类,将一直往上追溯到object类(顶级父类)。
i、子类最多只能继承一个父类,即Java中的单继承机制。如果要让A继承B类和C类呢?【A继承B,B继承C】。
j、不能滥用继承,子类和父类必须满足is-a的逻辑关系:如 猫是动物。
继承本质详解:
public class Extends {
public static void main(String[] args) {
Son son = new Son();
/**
此时需要根据查找关系来返回信息:
a、查看子类是否有该属性。
b、如果子类有,且可以访问,则返回信息。
c、子类没有,看父类(父类有且可以访问返回父类,如果是私有则需要转换一下访问)。
d、如果父类没有,则继续网上找,知道object。
**/
system.out.println(son.name);
system.out.println(son.age);
system.out.println(son.getSal);
}
}
class Grandpa {
String name = "yeye";
int age = 60;
}
class Father extends Grandpa {
String name = "baba"'
int age = 30;
private double salary = 10000;
public double getSal() {
return salary;
}
}
class Son extends Father {
String name = "erzi";
}
继承练习题1:
public class Extends {
public static void main(String[] args) {
C c = new C(); // 输出结果为:
/*
我是A类
我是B类的有参构造
我是C类的有参构造
我是C类的无参构造
*/
}
}
class A {
public A() {
System.out.println("我是A类");
}
}
class B extends A{
public B() {
System.out.println("我是B类的无参构造");
}
public B(String name) {
// super(); // 此处有个隐藏的 super调用父类无参构造
System.out.println(name + "我是B类的有参构造");
}
}
class C extends B{
public C() {
this("hello"); // 1、调用本类的有参构造
System.out.println("我是C类的无参构造"):
}
public C(String name) {
super("hahaha"); // 2、调用父类的有参构造
System.out.println("我是C类的有参构造");
}
}
继承练习题2:
public class Test {
public static void main(String[] args) {
PC pc = new PC("inter", 128, 512, "AUSS");
pc.btand = "ASUS";
pc.getPcDetails();
NotePad notepad = new NotePad("inter", 128, 512,"black");
notepad.color = "black";
}
}
class Computer {
String CPU;
int Memory;
int HardDisk;
public Computer(String CPU, int Memory, int HardDisk) {
this.CPU = CPU;
this.Memory = Memory;
this.HardDisk = HardDisk;
}
public String getDetails() {
return CPU + "" + Memory + "" + HardDisk;
}
}
class PC extends Computer {
String btand;
// 继承时会报错?因为子类会默认调用父类无参构造器,
// 此时父类无参构造被覆盖了,子类要自己写
public PC(String CPU, int Memory, int HardDisk, String btand) {
super(CPU, Memory, HardDisk); // 调用父类有参构造
this.brand = brand;
}
public void getPcDetails() {
System.out.println(getDetails() + brand);
}
class NotePad extends Computer{
String color;
public PC(String CPU, int Memory, int HardDisk, String color) {
super(CPU, Memory, HardDisk);
this.color = color;
}
}
super关键字:
1、定义:super代表父类的引用,用于访问父类的属性、方法、构造器。
2、基本用法:
a、用于访问父类的属性,但不能访问父类的private属性:super.属性名;
b、用于访问父类的方法,但不能访问父类private方法:super.方法名(参数列表);
c、访问父类的构造器:super(参数列表); 只可以放在构造器第一句,只能出现一句。
3、意义及细节:
a、super调用父类构造器的意义(分工明确,父类的属性由父类初始化,子类的属性由子类初始化)。
class NotePad extends Computer {
private String color;
public NotePad(String cpu, int memory, int disk, String color) {
super(cpu, memory, disk); // 父类由父类初始化
this.color = color; // 子类由子类自己初始化
}
}
b、当子类和父类中的成员(属性和方法)重名时,为了访问父类成员,必须通过super,如果没有重名,则super、this以及直接访问都是一样的。
在子类中使用:直接访问、this、super三者的区别:
直接访问和this:
1、先找本类,本类有,则调用。
2、本类没有,找父类(如果有且可以调用,则调用;如果有但是不能调用,则报错)。
3、如果父类没有,则继续往上找,直到object类。一直没有找到,提示不存在。
super:
1、直接找父类,不找本类。其余规则如上。
class Computer {
private String cpu;
private int memory;
private int disk;
public int num = 12;
public Computer(String cpu, int memory, int disk) {
this.cpu = cpu;
this.memory = memory;
this.disk = disk;
}
}
class NotePad extends Computer {
private String color;
public int num = 1;
public void test() {
System.out.println(num); // 1
System.out.println(this.num); // 1
System.out.println(super.num); // 12
}
}
c、super访问不限于直接父类,如果爷爷类也有同名成员,也可以使用super去访问。如果多个上级类都有同名成员,遵循就近原则。
继承中的方法重写/覆盖:
1、定义:方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么就是子类方法覆盖了父类方法。
2、需要满足的条件:
a、子类的方法的形参列表、方法名称要和父类的参数方法名称完全一样。
b、子类方法的返回类型要和父类一样或者是父类返回类型的子类:如父类返回类型是object,子类是String。
c、子类方法不可以缩小父类方法的访问权限:public > protected > 默认 > private。但是可以扩大,如父类是protected,子类可以一样或者是public
方法重写和方法重载的区别:
1、方法重载:Java中允许在同一个类中,多个同名方法的存在,到要求形参列表不一致。
2、方法重写:子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么就是子类方法覆盖了父类方法。
多态:
一、多态概述:
1、多态是继封装、继承之后,面向对象的第三大特性。
2、多态现实意义理解:
现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学张三既是学生也是人,即出现两种形态。
Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。
3、多态的具体体现:
对象的多态:
a、一个对象的编译类型和运行类型可以不一致。
Animal animal = new Dog(); // animal的编译类型是Animal,运行类型是Dog
b、编译类型在定义对象时,就确定了,不可改变。
c、运行类型是可以变化的。
animal = new Cat(); // 编译类型仍然是Animal,运行类型变为了Cat.
d、编译类型看定义时 = 号左边,运行类型看 = 号右边。
4、多态体现为父类引用可以指向子类对象。
Master tom = new Master("Tom");
Dog dog = new Dog("大黄");
Bone bone = new Bone("骨头");
tom.feed(dog, bone);
Cat cat = new Cat("小花");
Fish fish = new Fish("小鱼");
tom.feed(cat, fish);
// 因为父类的引用可以指向子类的对象。所以:不管上面有几个对象,方法只用一个就行
// Animal animal = new Dog("大黄");
// Animal animal = new Cat("小花");
// Food food = new Bone("骨头");
// Food food = new FIsh("小鱼");
public void feed(Animal animal, Food food) {
System.out.println(name + "给" + animal.getName() + "吃" + food.getFood());
}
多态细节
多态的前提条件:两个对象(类)必须有 继承关系.
1、多态的向上转型:
a、本质:父类的引用指向了子类的对象。
b、语法:父类 引用名 = new 子类类型();
c、特点:
①、编译类型看左边,运行类型看右边。
②、可以调用父类中所有的成员(方法、属性),要遵守访问权限。
③、不能调用子类中特有的成员。
④、最终运行效果看子类的具体实现。
Animal animal = new Cat();
// 1、可以调用父类中所有的成员(方法、属性),要遵守访问权限。
animal.animalEat();
animal.animalSleep();
// 2、不能调用子类中特有的成员。
// animal.catEat(); 错误
// 因为在编译阶段,能调用哪些成员由编译类型来决定,编译看左边 Animal,Animal没有catEat方法,不可调用。
// 3、最终运行效果看子类的具体实现,即调用方法时,从子类开始查找方法,子类有调用子类,子类没有找父类,有则调用,无则继续往上找。
animal.eat(); // 子类有eat方法,调用子类
animal.run(); // 子类无run方法,找父类。
/*
向上转型调用方法规则:
2、多态的向下转型:
a、语法:子类类型 引用名 = (子类类型) 父类引用;
Cat cat = (Cat) animal;
b、只能强转父类的引用,不能强转父类的对象。
Cat cat = (Cat) Animal; // 错误
c、要求父类的引用必须指向的是当前目标类型的对象。
即先要父类的引用指向子类的对象之后,才能再向下转型。
Animal animal = new Cat(); // 先有这个
Cat cat = (Cat) animal; // 才能向下转型
d、向下转型后,可以调用子类中所有成员。
3、多态属性及instanceOf
a、属性没有重写之说,属性的值看编译类型。
Base base = new Sub();
Systm.out.println(base.count); // 看编译类型Base的值
b、instanceOf比较操作符:用于判断对象的 运行类型 是否为某某某的类型或者子类型。
System.out.println(base instanceOf Base); // true
System.out.println(base instanceOf Sub); // base 运行类型为sub true
public class instanceOf {
public static void main(String[] args) {
BB bb = new BB();
System.out.println(bb instanceof BB); // true
System.out.println(bb instanceof AA); // true
// aa的编译类型是AA,aa的运行类型是BB
AA aa = new BB();
// 下面这句话意思:aa的运行类型BB 是 AA 的类或者子类吗?
System.out.println(aa instanceof AA); // true
// 下面这句话意思:aa的运行类型BB 是 BB 的类或者子类吗?
System.out.println(aa instanceof BB); // true
}
}
class AA {}
4、动态绑定机制:
a、当调用对象的方法时,该方法会和该对象的内存地址/运行类型绑定。
b、当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用。
// 父类
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;
}
}
//主类:
A a = new B();
/*
1、子类找sum,找不到找父类。
2、父类有getI方法,子类也有那么调用哪个呢?该方法会和该对象的内存地址/运行类型绑定
运行类似是B,所以会调用子类B的getI方法。
*/
System.out.println(a.sum()); // 20 + 10 = 30
System.out.println(a.sum1()); // 属性没有动态绑定,就近原则 10 + 10 = 20
1、多态数组:
题目要求:一个person类。里面 name,age属性。一个student类。里面特有score属性和一个study方法,一个teacher类,里面特有salary属性,两个类都继承person类,分别调用里面的say()方法和各自特有方法。
Person persons[] = new Person[5];
persons[0] = new Person("jack", 20);
persons[1] = new Student("tom", 20, 100);
persons[2] = new Student("smith", 20, 100);
persons[3] = new Teacher("linda", 31, 22100);
persons[4] = new Teacher("king", 33, 22300);
for(int i = 0; i < persons.length; i++) {
System.out.println(persons[i].say()); // say方法
if(persons[i] instanceOf Student) { // 使用了类型判断
Student student = (Student)persons[i]; // 使用向下转型调用子类特有方法
student.study();
} else if(persons[i] instanceOf Teacher) {
(Teacher)persons[i].teacher();
}
}
2、多态参数:
方法的形参类型为父类类型,实参为子类类型:
public void feed(Animal animal, Food food) { // 形参为父类
System.out.println(name + "给" + animal.getName() + "吃" + food.getFood());
Master tom = new Master("Tom");
Dog dog = new Dog("大黄");
Bone bone = new Bone("骨头");
tom.feed(dog, bone); // 实参为子类
Cat cat = new Cat("小花");
Fish fish = new Fish("小鱼");
tom.feed(cat, fish);
题目要求:
public class Main {
public static void main(String[] args) {
Ordinary tom = new Ordinary("tom", 2500);
Manager milan = new Manager("milan", 5000, 200000);
Main main = new Main();
main.showEmpAnnual(tom);
main.showEmpAnnual(milan);
main.testWork(tom);
main.testWork(milan);
}
// 方法:输出普通员工和经理的工资
public void showEmpAnnual(Employee e) {
System.out.println(e.getAnnual());
}
// 添加一个方法:如果是普通员工,则调用work方法,如果是经理,则调用manage方法
// 使用 类型判断 + 向下转型 这里 e 就是 Employee的引用类型 Employee e =
public void testWork(Employee e) {
if (e instanceof Ordinary) {
/*
下面这句相当于:
Ordinary ordinary = (Ordinary)e;
ordinary.work;
的简写
* */
((Ordinary) e).work(); // 向下转型
} else if (e instanceof Manager) {
((Manager) e).manage();
} else {
System.out.println("不处理");
}
}
}
object类:
object是所有类的超类。
1、 == 和 equals对比
①、 ==
a、== 是一个比较运算符,既可以判断基本类型,也可以判断引用类型。
b、如果判断基本类型,判断的是值是否相等;如果判断的是引用类型,判断的是地址是否相等,即判断是否是同一个对象。
②、equals:
a、是object类中的方法,只能判断引用类型。
b、默认判断地址是否相等,子类往往会重写该方法,用来判断内容是否相等。