Java面对对象进阶
# Java面向对象进阶
# 学习内容
static
继承
包、final、权限修改、代码块
抽象类
接口
多态
内部类
拼图小游戏
# Static
# Static修饰符
static表示静态,是Java中的一个修饰符,可以修饰成员方法,成员变量。
- 被static修饰的成员变量,叫做静态变量
- 被static修饰的成员方法,叫做静态方法
示例:
public class Stu {
String name; //在类中定义非静态常量
int age; //在类中定义非静态常量
static String teacherName; //在类中定义静态常量
public void show(){
System.out.println(name + "," + age + ","
+ teacherName);
}
public static void method(){
System.out.println("这是静态方法" + teacherName);
}
}
静态变量
特点:
被该类所有的对象共享
不属于对象,属于类
随着类的加载而加载,优先于对象存在
调用方式:
类名调用(推荐)
对象名调用
静态方法
特点:
多用在测试类和工具类当中
javabean类中很少会用
调用方式:
类名调用(推荐)
对象名调用
# Static工具类
有三种类,分别是以下:
Javabean类
: 用来描述一类事物的类,比如,Student,Teacher,Dog,Cat等测试类
: 用来检查其他类是否书写正确,带有main的方法的类,是程序的入口工具类
: 不是用来描述一类事物的,而是帮我们做一些事情的类
格式规范:
- 类名见知意
- 私有化构造方法
- 方法定义为静态
public class ArrUtil {
private ArrUtil(){
public static int getMax(...){...}
public static int getMin(...){...}
public static int getSum(...){...}
}
}
定义数组的工具类
需求: 在实际的开发中,经常会遇到一些数组使用的工具类
请按照如下的要求编写一个数组的工具类: ArrayUtil
- 提供一个工具类方法printArr,用于返回整数数组的内容。
- 返回的字符串格式如: [10,20,50,34,100] (只考虑整数数组,且只考虑一堆数组)
- 提供这样一个工具方法getAerage,用于返回平均分。 (只考虑浮点型数组,且只考虑一堆数组)
- 定义一个测试类TestDemo,调用该工具类的工具方法,并返回结果。
package a01staticdemo1;
public class ArrayUtil {
// 私有化构造方法
// 目的: 为了不让外界创建他的对象
private ArrayUtil(){
}
// 需要定义为静态,方便调用
public static String printArr(int[] arr){
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < arr.length; i++) {
if (i == arr.length -1){
sb.append(arr[i]);
}else {
sb.append(arr[i]).append(",");
}
}
sb.append("]");
return sb.toString();
}
public static double getAverage(double[] arr){
double sum = 0;
for (int i = 0; i < arr.length; i++) {
sum = sum + arr[i];
}
return sum / arr.length;
}
}
使用TestDemo测试类:
package a01staticdemo1;
public class TestDemo {
public static void main(String[] args) {
int[] arr1 = {1,2,3,4,5};
String str = ArrayUtil.printArr(arr1);
System.out.println(str);
double[] arr2 = {1.5,3.7,4.9,5.8};
double avg = ArrayUtil.getAverage(arr2);
System.out.println(avg);
}
}
以上代码返回的结果:
[1,2,3,4,5]
3.9750000000000005
注意: 工具类的作用就是帮助我们解决各种需要的事情。
# Static的注意事项
- 静态方法
只能
访问静态变量和静态方法 - 非静态方法
可以
访问静态变量或者静态方法,也可以
访问非静态成员的变量和非静态的成员方法 - 静态方法中是没有this关键字
总结:
- 静态方法中,只能访问静态。非静态方法中可以访问所有。静态方法中没有this关键字。
Static的main方法
public class HelloWord {
public static void main(String[] args) {
System.out.println("HelloWord");
}
}
public:
被JVM调用,访问权限足够大static:
被JVM调用,不要创建对象,直接类名访问,因为main方法是静态的 所以测试类中其他方法也是需要静态的。
void:
被JVM调用,不需要给JVM返回值main:
一个通用的名称,虽然不是关键字,但是被JVM识别String[] args:
以前用于接收键盘录入数据的,现在没用
# 封装
# 封装的概述
在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法
。
封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
要访问该类的代码和数据,必须通过严格的接口控制。
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
# 封装的优点
- 良好的封装能够减少耦合。
- 类内部的结构可以自由修改。
- 可以对成员变量进行更精确的控制。
- 隐藏信息,实现细节。
# 实现Java封装
修改属性的可见性来限制对属性的访问(一般限制为private),例如:
public class Person {
private String name;
private int age;
}
这段代码中,将 name
和 age
属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。
对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问,例如:
public class Person{
private String name;
private int age;
public int getAge(){
return age;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public void setName(String name){
this.name = name;
}
}
注意:
采用 this 关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。
# 封装的实例
package day13;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = 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;
}
}
以上实例中public方法是外部类访问该类成员变量的入口。
通常情况下,这些方法被称为getter和setter方法。
因此,任何要访问类中私有成员变量的类都要通过这些getter和setter方法。
package day13;
public class PersonTest {
public static void main(String[] args) {
Person person = new Person("小明",12);
System.out.println(person.getName());
System.out.println(person.getAge());
person.setAge(18);
System.out.println(person.getAge());
}
}
以上代码返回的结果:
小明
12
18
# 继承
# 继承的概念
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
- Java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起继承关系。
public class Student extends Person { }
- Student称为子类(派生类),Person称为父类(基类或超类)。
使用继承的好处:
- 可以把多个子类中的重复的代码抽取到父类中了,提高代码的复用性。
- 子类可以在父类基础上,增加其他的功能,使子类更强大。
# 继承的特性
- 子类拥有父类非 private 的属性、方法。
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
- Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
- 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
继承的特点总结:
- Java只支持单继承、不支持多继承、但是支持多层继承
- Java中所有的类都直接或者间接继承于object
- 子类只能访问父类中非私有的成员
继承中
成员方法的访问特点
- 直接调用满足就近原则,谁离我近,我就用谁
- super调用,直接访问父类
# 继承关键字
继承可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang
包中,所以不需要 import
)祖先类。
# extends关键字
在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。
public class Animal {
private String name;
private int id;
public Animal(String myName, int myid) {
//初始化属性值
}
public void eat() { //吃东西方法的具体实现 }
public void sleep() { //睡觉方法的具体实现 }
}
public class Penguin extends Animal{
}
# super 与 this 关键字
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
this关键字:指向自己的引用。
this和supper的使用:
public class Test {
public static void main(String[] args) {
Zi zi = new Zi();
zi.ziShow();
}
}
class Fu{
String name = "super";
}
class Zi extends Fu{
String name = "this";
public void ziShow(){
String name = "ziShow";
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);
}
}
以上代码返回的结果:
ziShow
this
super
继承中
构造方法的访问特性:
- 父类中的构造方法不会被子类继承,但是可以用super调用
- 子类构造方法的第一行,有一个默认的super()
- 子类中所有的构造方法默认先访问父类中的无参构造,再执行自己
- 如果需要方法父类有参构造,必须手动书写
this、super使用总结
this: 理解为一个变量,表示当前方法调用者的地址值
super: 代表父类存储空间
# 方法的重写
当父类的方法不能满足子类的方法要求,需要进行方法重写。
在继承中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的方法。
@Override重写注解
@Override
是放在重写后的方法上,校验子类重写时语法是否正确。- 加上注解后如果有红色波浪线,表示语法有错误
建议重写方法都加@Override注解,代码安全,优雅!
public class Demo {
public static void main(String[] args) {
Student stu = new Student();
stu.lunch();
}
}
class Person {
public void eat(){
System.out.println("吃饭");
}
public void drink(){
System.out.println("喝水");
}
}
class Student extends Person{
public void lunch(){
this.eat();
this.drink();
super.eat();
super.drink();
}
@Override
public void eat(){
System.out.println("吃意大利面");
}
@Override
public void drink(){
System.out.println("喝牛奶");
}
}
以上代码返回的结果:
吃意大利面
喝牛奶
吃饭
喝水
方法重写的注意事项:
- 重写方法的每次、形参列表必须与父类中的一致
- 子类重写父类方法时候,访问权限子类必须大于等于父类
- 子类重写父类方法时,返回值类型子类必须小于等于父类
建议: 重写的方法尽量和父类的保持一致
- 只有被添加到虚方法列表中的方法才能被重写
利用方法的重写设计继承结构
现在有三种动物:哈士奇、沙皮狗、中华田园犬
暂时不考虑属性,只考虑行为
请按照继承的思想特点进行继承体系的设计
三种动物分别有以下行为:
- 哈士奇: 吃饭(吃狗粮) 喝水 看家 拆家
- 沙皮狗: 吃饭(吃狗粮、吃骨头) 喝水 看家
- 中华田园犬 吃饭(吃剩饭) 喝水 看家
分别写如下几个类:
- Dog
- Hasky
- Shapidog
- Chinadog
父类的Dog如下:
package Dog;
public class Dog {
public void eat(){
System.out.println("在吃狗粮");
}
public void drink(){
System.out.println("在喝水");
}
public void lookHome(){
System.out.println("在看家");
}
}
分别写三个子类的内容:
package Dog;
public class Hasky extends Dog{
public void brokeHome(){
System.out.println("在拆家");
}
}
package Dog;
public class ChinaDog extends Dog{
@Override
public void eat(){
System.out.println("在吃饭,吃着剩饭");
}
}
package Dog;
public class Shapidog extends Dog{
@Override
public void eat(){
System.out.println("在吃饭,吃着狗粮和骨头");
}
}
创建一个测试类:
package Dog;
public class Test {
public static void main(String[] args) {
Hasky h = new Hasky();
h.eat();
h.brokeHome();
h.drink();
ChinaDog chinaDog = new ChinaDog();
chinaDog.eat();
chinaDog.drink();
chinaDog.lookHome();
Shapidog shapidog = new Shapidog();
shapidog.eat();
shapidog.drink();
shapidog.lookHome();
}
}
以上代码返回的结果:
在吃狗粮
在拆家
在喝水
在吃饭,吃着剩饭
在喝水
在看家
在吃饭,吃着狗粮和骨头
在喝水
在看家
设计继承构造方法
1.经理
成员变量: 工号,姓名,工资,管理奖金
成员方法: 工作(管理其他人),吃饭(吃米饭)
2.厨师
成员变量: 工号,姓名,工资
成员方法:工作(炒菜),吃饭(吃米饭)
创建四个类:
- Employee
- Manager
- Cook
- test
创建父类Employee,作为主要参数构造
package dmeo;
public class Employee {
// 类名见名知意
// 所有的成员变量都需要私有
// 构造方法
private String id;
private String name;
private double salary;
public Employee() {
}
public Employee(String id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public void work(){
System.out.println("在工作");
}
public void eat(){
System.out.println("在吃饭");
}
}
创建经理和厨师的子类:
package dmeo;
public class Manager extends Employee{
private double bouns;
public Manager() {
}
// 带全部参数的构造
// 父类 + 子类
public Manager(String id, String name, double salary, double bouns) {
super(id, name, salary);
this.bouns = bouns;
}
public double getBouns() {
return bouns;
}
public void setBouns(double bouns) {
this.bouns = bouns;
}
@Override
public void work(){
System.out.println("在管理其他人");
}
}
package dmeo;
public class Cook extends Employee {
public Cook() {
}
public Cook(String id, String name, double salary) {
super(id, name, salary);
}
@Override
public void work(){
System.out.println("厨师正在炒菜");
}
}
创建测试类:
package dmeo;
public class test {
public static void main(String[] args) {
Manager m = new Manager("heima001","张三",15000,8000);
System.out.println(m.getId() + "," + m.getName() + "," + m.getSalary() + "," + m.getBouns());
m.work();
m.eat();
Cook c = new Cook();
c.setId("heima002");
c.setName("李四");
c.setSalary(8000);
System.out.println(c.getId() + "," + c.getName() + "," + c.getSalary());
c.work();
c.eat();
}
}
以上的代码返回的结果:
heima001,张三,15000.0,8000.0
在管理其他人
在吃饭
heima002,李四,8000.0
厨师正在炒菜
在吃饭
# 多态
# 多态的概念
什么是多态?
同类型的对象,表现出不同的形态。
多态性是对象多种表现形式的体现。
现实中,比如我们按下 F1 键这个动作:
- 如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;
- 如果当前在 Word 下弹出的就是 Word 帮助;
- 在 Windows 下弹出的就是 Windows 帮助和支持。
同一个事件发生在不同的对象上会产生不同的结果。
多态的表现形式:
父类类型 对象名称 = 子类对象
# 多态的优点
- 消除类型之间的耦合关系
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性
多态的前提:
有继承/实现关系
有父类引用指向子类对象
有方法重写
多态的好处:
- 使用父类型作为参数,可以接受所有子类对象
- 体现多态的扩展性与便利
多态的优势
方法中,使用父类作为参数,可以接受所有子类对象
多态的弊端
不能使用子类的特有功能
引用数据类型的类型转换,有几种方式
- 自动类型转换 强制类型转换
Person p = new Student();
Student s = (Person) p;
强制类型转换能解决什么问题?
- 可以转换成真正的子类类型,从而调用子类独有功能
- 转换类型与真实对象类型不一致会报错
- 转换的时候用instanceofl关键字进行判断
if (animal instanceof Dog dog){
dog.lookHome();
}else if (animal instanceof Cat cat){
cat.catchMouse();
}
多态的示例:
package day02;
public class TEST {
public static void main(String[] args) {
// 创建对象(多态方式)
// Fu a = new Dog();
Animal a = new Dog();
// 动物
// 调用成员变量: 编译看左边 运行也看左边
// 编译看左边: javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有编译失败
// 运行也看左边: java运行代码的时候,实际获取的就是左边的父类成员变量的值
System.out.println(a.name); // 动物
// 调用成员方法: 编译看左边,运行看右边
// 编译看左边: java编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有编译失败
// 运行看右边: java运行代码的时候,实际上运行的是子类的方法
a.show(); // Dog --- show 方法
// 成员变量: 在子类的对象中,会把父类的成员变量也继承下去的 父: name 子: name
// 成员方法: 如果子类对方进行了重写,那么虚方法表中是会把父类的方法进行覆盖的。
}
}
class Animal{
String name = "动物";
public void show(){
System.out.println("Animal --- show方法");
}
}
class Dog extends Animal{
String name = "狗";
@Override
public void show(){
System.out.println("Dog --- show方法");
}
}
以上代码返回的结果:
动物
Dog --- show方法
如上的示例可以看出:
调用成员变量
调用成员变量
: 编译看左边 运行也看左边编译看左边
: javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有编译失败运行也看左边
: java运行代码的时候,实际获取的就是左边的父类成员变量的值
调用成员方法
调用成员方法
: 编译看左边,运行看右边编译看左边
: java编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有编译失败运行看右边
: java运行代码的时候,实际上运行的是子类的方法
# 多态的综合练习
1.定义狗类
属性: 年龄、颜色
行为: eat(String something)、看家lookhome的方法
2.定义猫类
属性: 年龄、颜色
行为:eat(String something)、抓老鼠cattchMouse方法
3.定义Person类/饲养员
属性:姓名、年龄
行为:keepPet(Dog dog,String something)、功能养宠物、something表示喂养的东西。
定义Animal父类:
public class Animal {
private int age;
private String color;
public Animal() {
}
public Animal(int age, String color) {
this.age = age;
this.color = color;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void eat(String something){
System.out.println("在吃饭");
}
}
定义Dog和Cat的子类:
public class Dog extends Animal{
public Dog() {
}
public Dog(int age, String color) {
super(age, color);
}
@Override
public void eat(String something) {
System.out.println(getAge() + "岁的"+ getColor() +"的狗两只前腿死死的抱住" + something +"吃");
}
public void lookHome(){
System.out.println("狗在看家");
}
}
public class Cat extends Animal{
public Cat() {
}
public Cat(int age, String color) {
super(age, color);
}
@Override
public void eat(String something) {
System.out.println(getAge() + "岁的" + getColor() + "的猫眯着眼睛侧着头吃" + something);
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
定义Person的类:
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = 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;
}
// 饲养狗
// public void keepPet(Dog dog,String something){
// System.out.println("年龄为" + age + "岁的" + name + "养了一只" + dog.getColor() + "颜色" + dog.getAge() + "岁的狗");
// dog.eat(something);
// dog.lookHome();
// }
//
// // 饲养猫
// public void keepPet(Cat cat,String something){
// System.out.println("年龄为" + age + "岁的" + name + "养了一只" + cat.getColor() + "颜色" + cat.getAge() + "岁的猫");
// cat.eat(something);
// cat.catchMouse();
// }
// 想要一个方法,接受所有动物,包括猫,包括狗
public void keepPet(Animal animal,String something){
if (animal instanceof Dog dog){
System.out.println("年龄为" + age + "岁的" + name + "养了一只" + dog.getColor() + "颜色" + dog.getAge() + "岁的狗");
dog.eat(something);
dog.lookHome();
}else if (animal instanceof Cat cat){
System.out.println("年龄为" + age + "岁的" + name + "养了一只" + cat.getColor() + "颜色" + cat.getAge() + "岁的猫");
cat.eat(something);
cat.catchMouse();
}
}
}
定义一个测试类:
public class test {
public static void main(String[] args) {
// 创建对象和调用方法
Person p1 = new Person("老王",30);
Dog d = new Dog(2,"黑色");
Cat c = new Cat(3,"灰色");
p1.keepPet(d,"骨头");
p1.keepPet(c,"鱼");
}
}
以上代码返回的结果:
年龄为30岁的老王养了一只黑色颜色2岁的狗
2岁的黑色的狗两只前腿死死的抱住骨头吃
狗在看家
年龄为30岁的老王养了一只灰色颜色3岁的猫
3岁的灰色的猫眯着眼睛侧着头吃鱼
猫抓老鼠
# 包和final
# 包的概述
包就是文件夹。用来管理各种不同功能的Java类,方便后期代码维护。 包名的规则:公司域名反写+包的作用,需要全部英文小写,见名知意。com.itheima.domain
package com.itheima.com.domain;
public class Student {
私有化成员变量;
构造方法;
成员方法;
}
如下是方法的说明
com.itheima.domain.Student
,全称是全类名,其中Student
是全限定名。
# 使用其它类的规则
- 使用同一个包中的类时,不需要导包。
- 使用java.lang包中的类时,不需要导包。
- 其它情况都需要导包
- 如果同时使用两个包中的同名类,需要用全类名。
使用其他类时候,需要使用全类名。
import com.itheima.domain.Student;
public class Test {
public static void main(String[] args) {
com.itheima.domain.Student s = new com.itheima.domain.Student();
}
}
# final
final 表示"最后的、最终的"含义,变量一旦赋值后,不能被重新赋值。被 final 修饰的实例变量必须显式指定初始值。
final 修饰符通常和 static 修饰符一起使用来创建类常量。
修饰方法
: 表明该方法是最终方法,不能被重写修饰类
: 表明该类是最终类,不能被继承修饰变量
: 叫做常量,只能被赋值一次
final 方法
- 父类中的 final 方法可以被子类继承,但是不能被子类重写。声明 final 方法的主要目的是防止该方法的内容被修改。
final 类
- final 类不能被继承,没有类能够继承 final 类的任何特性。
使用方法:
// 不能被继承
final class Fu {
// 不能被重写
public final void show(){
// 不能再赋值
final int i = 10;
System.out.println(i);
}
}
# 常量
实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性。 常量的命名规范:
- 单个单词: 全部大写
- 多个单词:全部大写,单词之间用下划线隔开
细节:
final修饰的变量是基本类型,那么变量的存储的数据值
不能发生改变。
final修饰的变量是引用类型,那么变量的存储地址值
不能发生改变,对象内部的可以改变。
提高可读性的示例:
public class StudentSystem {
private static final String ADD_STUDENT = "1";
private static final String DELETE_STUDENT = "2";
private static final String UPDATE_STUDENT = "3";
private static final String CHECK_STUDENT = "4";
private static final String EXIT = "5";
switch (chose){
case ADD_STUDENT -> addStudent(list);
case DELETE_STUDENT -> deleteStudent(list);
case UPDATE_STUDENT -> setStudent(list);
case CHECK_STUDENT -> getStudent(list);
case EXIT -> {
System.out.println("退出");
// break loop;
System.exit(0); // 停止虚拟机运行
}
default -> System.out.println("没有这个选项");
}
}
# 权限修饰符
# 修饰符的概述
- 权限修饰符:是用来控制一个成员能够被访问的范围的
- 可以修饰成员变量,方法,构造方法,内部类。
public class Student{
private String name;
private int age;
}
# 权限修饰符的分类
有四种作用范围由小到大(private
< 空着不写
< protected
< public
)
访问控制修饰符 Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。
default
: (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。private
: 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)public
: 对所有类可见。使用对象:类、接口、变量、方法protected
: 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
# 修饰符访问权限
修饰符 | 当前类 | 同一包内 | 子孙类(同一包) | 子孙类(不同包) | 其他包 |
---|---|---|---|---|---|
public | Y | Y | Y | Y | Y |
protected | Y | Y | Y | Y/N(说明 (opens new window)) | N |
default | Y | Y | Y | N | N |
private | Y | N | N | N | N |
实际开发中,一般只用private和public
- 成员变量私有
- 方法公开
特例:如果方法中的代码是抽取其它方法中共性代码,这个方法一般也私有
# 代码块
代码块分别有如下三种类型
- 局部代码块
- 构造代码块
- 静态代码块
# 局部代码块
该技术已经淘汰,提前结束变量的生命周期,不够灵活
public class demo{
public static void main(String[] args) {
{
int a = 10;
}
// 因为当代码执行到这里的时候,变量a就从内存中消失了
// System.out.println(a);
}
}
# 构造代码块
什么是构造代码块?
- 写在成员位置的代码块
- 作用是可以把多个构造方法中重复的代码取出来
- 执行时机,我们在创建本类对象的时候会先执行构造代码块再执行构造方法
public class demo{
private String name;
private int age;
// 构造代码块
{
System.out.println("开始创建对象了");
}
public demo{
// this(null,0);
}
public demo(String name;int age){
System.out.println("开始创建对象了");
this.name = name;
this.age = age;
}
}
# 静态代码块
- 格式: static{}
- 特点: 需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次
- 使用场景: 在类加载的时候,做一些数据初始化的时候使用。
数据的初始化:
public class demo{
private String name;
private int age;
// 构造代码块
static {
System.out.println("开始创建对象了");
}
public demo{
// this(null,0);
}
public demo(String name;int age){
System.out.println("开始创建对象了");
this.name = name;
this.age = age;
}
}
# 抽象类
# 抽象类概述
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具 体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
在 Java 中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
抽象类和抽象方法
抽象类的定义格式
抽象方法的定义格式
子类继承抽象类之后,如何重写抽象方法
# 抽象方法
抽象方法:将共性的
行为(方法)抽取到父类之后。由于每一个子类执行的内容是不一样的,所以,在父类中不能确定具体的方法体。该方法就可以定义为抽 象方法。
抽象类:如果一个类中存在抽象方法
,那么该类就必须声明为抽象类
抽象类和抽象方法的注意事项
- 抽象类不能实例化(创建对象)
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 可以有构造方法
- 抽象类的子类
- 要么重写抽象类的所有抽象方法
- 要么是抽象类
# 定义格式
抽象方法的定义格式:
public abstract 返回值类型 方法名(参数列表);
抽象类的定义格式:
public abstract class 类名{}
抽象类和抽象方法的意义
编写带有抽象类的标准Javabean类 青蛙frog 属性:名字、年龄 行为:吃虫子、喝水 狗Dog 属性:名字、年龄 行为:吃骨头、喝水 山羊Sheep 属性:名字、年龄 行为:吃艹、喝水
创建Animal的父类:
package demo;
public abstract class Animal {
private String name;
private int age;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = 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;
}
public void drink(){
System.out.println("动物在喝水");
}
public abstract void eat();
}
创建三个子类:
package demo;
public class Dog extends Animal{
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
package demo;
public class Frog extends Animal{
public Frog() {
}
public Frog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("青蛙在吃虫子");
}
}
package demo;
public class Sheep extends Animal{
public Sheep() {
}
public Sheep(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("山羊在吃草");
}
}
创建测试类:
package demo;
public class test {
public static void main(String[] args) {
Frog f = new Frog("小绿",1);
f.eat();
f.drink();
}
}
以上代码返回的结果:
青蛙在吃虫子
动物在喝水
# 接口
# 接口概述
接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
为什么有接口?
接口
就是一种规则。
# 接口与类相似点
- 一个接口可以有多个方法。
- 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
- 接口的字节码文件保存在 .class 结尾的文件中。
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
# 接口与类的区别
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承。
# 接口特性
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
# 抽象类和接口的区别
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
# 接口的使用
接口的定义设使用:
- 接口用关键字interface来定义
- public
interface
接口名 {} - 接口不能实例化
- 接口和类之间实现关系,通过implements关键字表示public class 类名
implements
接口名 {}
定义一个父类Animal,再定义一个Swim的接口,创建Frog的子类,通过子类调用swim的接口。
public abstract class Animal {
private String name;
private int age;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = 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;
}
public abstract void eat();
}
创建Swim的接口:
public interface Swim {
public void swim();
}
创建子类Frog:
public class Frog extends Animal implements Swim{
@Override
public void eat() {
System.out.println("青蛙吃东西");
}
@Override
public void swim() {
System.out.println("青蛙在游泳");
}
public static void main(String[] args) {
Frog frog = new Frog();
frog.eat();
frog.swim();
}
}
以上代码返回的结果是:
青蛙吃东西
青蛙在游泳
# 接口中成员的特点
成员变量
- 只能是常量
- 默认修饰符:
public
static final
构造方法
- 没有
成员方法
- 只能是抽象方法
- 默认修饰符: public abstract
JDK7以前: 接口中只能定义抽象方法
在JDK8以后新增了那些方法?
- 默认方法: default修饰,实现类对象调用
- 静态方法: static修饰,必须用当前接口名调用
- 私有方法: private修饰,jdk9开始才有的,只能在接口内部被调用
# 接口和类之间的关系
类和类的关系
- 继承关系,只能单继承,不能多继承,但是可以多层继承
类和接口的关系
- 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
接口和接口的关系