类的定义格式如下:
修饰符 class 类名 {
// 1.成员变量(属性)
// 2.成员方法 (行为)
// 3.构造方法 (初始化类的对象数据的)
}
例如:
public class Student {
// 1.成员变量
public String name ;
public char sex ; // '男' '女'
public int age;
}
类名 对象名称 = new 类名();
例如:
Student stu = new Student();
1.使用 private
关键字来修饰成员变量。
2.使用public
修饰getter和setter方法。
public class Student {
private String name;
private int age;
}
public class Student {
private String name;
private int age;
public void setName(String n) {
name = n;
}
public String getName() {
return name;
}
public void setAge(int a) {
if (a > 0 && a <200) {
age = a;
} else {
System.out.println("年龄非法!");
}
}
public int getAge() {
return age;
}
}
在创建对象的时候,给成员变量进行初始化。
初始化即赋值的意思。
修饰符 类名(形参列表) {
// 构造体代码,执行代码
}
首先定义一个学生类,代码如下:
public class Student {
// 1.成员变量
public String name;
public int age;
// 2.构造方法
public Student() {
System.out.println("无参数构造方法被调用");
}
}
接下来通过调用构造方法得到两个学生对象。
public class CreateStu02 {
public static void main(String[] args) {
// 创建一个学生对象
// 类名 变量名称 = new 类名();
Student s1 = new Student();
// 使用对象访问成员变量,赋值
s1.name = "张三";
s1.age = 20 ;
// 使用对象访问成员变量 输出值
System.out.println(s1.name);
System.out.println(s1.age);
Student s2 = new Student();
// 使用对象访问成员变量 赋值
s2.name = "李四";
s2.age = 18 ;
System.out.println(s2.name);
System.out.println(s2.age);
}
}
this代表所在类的当前对象的引用(地址值),即代表当前对象。
this出现在实例方法中,谁调用这个方法(哪个对象调用这个方法),this就代表谁(this就代表哪个对象)。
public class Student {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
if (age > 0 && age < 200) {
this.age = age;
} else {
System.out.println("年龄非法!");
}
}
public int getAge() {
return age;
}
}
this出现在构造方法中,代表构造方法正在初始化的那个对象。
public class Student {
private String name;
private int age;
// 无参数构造方法
public Student() {}
// 有参数构造方法
public Student(String name,int age) {
this.name = name;
this.age = age;
}
}
Java中成员(变量和方法)等是存在所属性的,Java是通过static关键字来区分的。**static关键字在Java开发非常的重要,对于理解面向对象非常关键。
关于 static
关键字的使用,它可以用来修饰的成员变量和成员方法,被static修饰的成员是属于类的是放在静态区中,没有static修饰的成员变量和方法则是属于对象的。我们上面案例中的成员变量都是没有static修饰的,所以属于每个对象。
static是静态的意思。 static可以修饰成员变量或者修饰方法。
定义格式
修饰符 static 数据类型 变量名 = 初始值;
举例
public class Student {
public static String schoolName = "传智播客"; // 属于类,只有一份。
// .....
public static void study(){
System.out.println("我们都在黑马程序员学习");
}
}
静态成员变量的访问:
格式:类名.静态变量
格式:类名.静态方法
public static void main(String[] args){
System.out.println(Student.schoolName); // 传智播客
Student.schoolName = "黑马程序员";
System.out.println(Student.schoolName); // 黑马程序员
Student.study();
}
无static修饰的成员方法属于每个对象的,这个成员方法也叫做实例方法。
需要注意的是:实例方法是属于每个对象,必须创建类的对象才可以访问。
格式:对象.实例方法
示例:
public class Student {
// 实例变量
private String name ;
// 2.方法:行为
// 无 static修饰,实例方法。属于每个对象,必须创建对象调用
public void run(){
System.out.println("学生可以跑步");
}
// 无 static修饰,实例方法
public void sleep(){
System.out.println("学生睡觉");
}
public static void study(){
}
}
public static void main(String[] args){
// 创建对象
Student stu = new Student ;
stu.name = "徐干";
// Student.sleep();// 报错,必须用对象访问。
stu.sleep();
stu.run();
}
static的注意事项
小结:
1.当 static
修饰成员变量或者成员方法时,该变量称为静态变量,该方法称为静态方法。该类的每个对象都共享同一个类的静态变量和静态方法。任何对象都可以更改该静态变量的值或者访问静态方法。但是不推荐这种方式去访问。因为静态变量或者静态方法直接通过类名访问即可,完全没有必要用对象去访问。
2.无static修饰的成员变量或者成员方法,称为实例变量,实例方法,实例变量和实例方法必须创建类的对象,然后通过对象来访问。
3.static修饰的成员属于类,会存储在静态区,是随着类的加载而加载的,且只加载一次,所以只有一份,节省内存。存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。它优先于对象存在,所以,可以被所有对象共享。
4.无static修饰的成员,是属于对象,对象有多少个,他们就会出现多少份。所以必须由对象调用。
不是用来描述一类事物的,而是帮我们做一些事情的类。
工具类:
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++) {
//i 索引 arr[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;
}
}
测试类:
public class TestDemo {
public static void main(String[] args) {
//测试工具类中的两个方法是否正确
int[] arr1 = {1, 2, 3, 4, 5};
//直接就是:类名.f方法
String str = ArrayUtil.printArr(arr1);
System.out.println(str);
double[] arr2 = {1.5, 3.7, 4.9, 5.8, 6.6};
double avg = ArrayUtil.getAverage(arr2);
System.out.println(avg);
}
}
1.什么是继承、继承的好处?
继承是面向对象三大特征之一,可以让类跟类之间产生子父的关系。
继承**:就是子类继承父类的属性和行为,使得子类对象可以直接具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。
可以把多个子类中重复的代码抽取到父类中,子类可以直接使用,减少代码冗余,提高代码的复用性
提高代码的复用性(减少代码冗余,相同代码重复利用)。使类与类之间产生了关系。
2.继承的格式?
public class 子类 extends 父类{}
3.继承后子类的特点?
子类可以得到父类的属性和行为,子类可以使用。
子类可以在父类的基础上新增其他功能,子类更强大。继承的好处
子类不能继承的内容
通过 extends
关键字,可以声明一个子类继承另外一个父类,定义格式如下:
class 父类 {
...
}
class 子类 extends 父类 {
...
}
需要注意:Java是单继承的,一个类只能继承一个直接父类,跟现实世界很像,但是Java中的子类是更加强大的。
成员变量不重名:如果子类父类中出现不重名的成员变量,这时的访问是没有影响的。
成员变量重名:如果子类父类中出现重名的成员变量,这时的访问是有影响的。
super访问父类成员变量
1.继承中成员变量访问特点:就近原则。
先在局部位置找,本类成员位置找,父类成员位置找,逐级往上。
2.如果出现了重名的成员变量怎么办?
system.out.println(name); //从局部位置开始往上找
system.out.println(this.name) ; //从本类成员位置开始往上找
system.out.println(super.name); //从父类成员位置开始往上找
public class Test {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}
class Fu {
String name = "Fu";
String hobby = "喝茶";
}
class Zi extends Fu {
String name = "Zi";
String game = "吃鸡";
public void show() {
//如何打印Zi
//System.out.println(name);//Zi
//System.out.println(this.name);//Zi
//如何打印Fu
//System.out.println(super.name);//Fu
//如何打印喝茶
//System.out.println(hobby);//喝茶
//System.out.println(this.hobby);//喝茶
//System.out.println(super.hobby);//喝茶
//如何打印吃鸡
System.out.println(game);
System.out.println(this.game);
}
}
方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。
方法重写注意事项和要求:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0qgK2CFe-1665577746271)(E:\java\Java基础\图片\7\5.png)]
public class Test {
public static void main(String[] args) {
OverseasStudent s = new OverseasStudent();
s.lunch();
}
}
class Person {
public void eat() {
System.out.println("吃米饭,吃菜");
}
public void drink() {
System.out.println("喝开水");
}
}
//留学生
class OverseasStudent extends Person{
public void lunch(){
this.eat();//吃意大利面
this.drink();//喝凉水
super.eat();//吃米饭,吃菜
super.drink();//喝开水
}
//应用场景:当父类中方法,不能满足子类现在的需求时,我们就需要把这个方法进行重写。
//注意:子类中重写的方法上面需要加上@override
@Override
public void eat() {
System.out.println("吃意大利面");
}
@Override
public void drink() {
System.out.println("喝凉水");
}
}
class Student extends Person{
public void lunch(){
//先在本类中查看eat和drink方法,就会调用子类的,如果没有,就会调用从父类中继承下来的eat和drink方法
this.eat();
this.drink();
//直接调用父类中的eat和drink方法
super.eat();
super.drink();
}
}
继承中:构造方法的访问特点
为什么?
怎么调用父类构造方法的?
public class Person {
String name;
int age;
public Person() {
System.out.println("父类的无参构造");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Student extends Person{
public Student(){
//子类构造方法中隐藏的super()去访问父类的无参构造
super();
System.out.println("子类的无参构造");
}
public Student(String name,int age){
super(name,age);
}
}
public class Test {
public static void main(String[] args) {
//创建学生对象
//有参:经过有参构造方法,就不经过
Student s = new Student("zhangsan",23);
//无参:经过无参构造方法
Student s = new Student();
//输出: 父类的无参构造 子类的无参构造
}
}
代码如下:
class Person {
private String name;
private int age;
public Person() {
System.out.println("父类无参");
}
// getter/setter省略
}
class Student extends Person {
private double score;
public Student() {
//super(); // 调用父类无参,默认就存在,可以不写,必须再第一行
System.out.println("子类无参");
}
public Student(double score) {
//super(); // 调用父类无参,默认就存在,可以不写,必须再第一行
this.score = score;
System.out.println("子类有参");
}
}
public class Demo07 {
public static void main(String[] args) {
Student s1 = new Student();
System.out.println("----------");
Student s2 = new Student(99.9);
}
}
输出结果:
父类无参
子类无参
----------
父类无参
子类有参
super和this的用法格式
super和this完整的用法如下,其中this,super访问成员我们已经接触过了。
this.成员变量 -- 本类的
super.成员变量 -- 父类的
this.成员方法名() -- 本类的
super.成员方法名() -- 父类的
接下来我们使用调用构造方法格式:
super(...) -- 调用父类的构造方法,根据参数匹配确认
this(...) -- 调用本类的其他构造方法,根据参数匹配确认
小结
public class Student {
String name;
int age;
String school;
//需求:
//默认为传智大学
public Student() {
//表示调用本类其他构造方法
//细节:虚拟机就不会再添加super();
this(null,0,"传智大学");
}
public Student(String name, int age, String school) {
this.name = name;
this.age = age;
this.school = school;
}
}
public class Test {
public static void main(String[] args) {
Student s = new Student();
}
}
// 一个类只能有一个父类,不可以有多个父类。
class A {}
class B {}
class C1 extends A {} // ok
// class C2 extends A, B {} // error
// A可以有多个子类
class A {}
class C1 extends A {}
class C2 extends A {}
class A {}
class C1 extends A {}
class D extends C1 {}
顶层父类是Object类。所有的类默认继承Object,作为父类。
会写一个继承结构下的标准Javabean即可
需求:
猫:属性,姓名,年龄,颜色
狗:属性,姓名,年龄,颜色,吼叫
分享书写技巧:
1.在大脑中要区分谁是父,谁是子
2.把共性写到父类中,独有的东西写在子类中
3.开始编写标准Javabean(从上往下写)
4.在测试类中,创建对象并赋值调用
代码示例:
package com.itheima.test4;
public class Animal {
//姓名,年龄,颜色
private String name;
private int age;
private String color;
public Animal() {
}
public Animal(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
//get和set方法
}
public class Cat extends Animal{
//因为猫类中没有独有的属性。
//所以此时不需要写私有的成员变量
//空参
public Cat() {
}
//需要带子类和父类中所有的属性
public Cat(String name, int age, String color) {
super(name,age,color);
}
}
public class Dog extends Animal{
//Dog :吼叫
private String wang;
//构造
public Dog() {
}
//带参构造:带子类加父类所有的属性
public Dog(String name, int age, String color,String wang) {
//共性的属性交给父类赋值
super(name,age,color);
//独有的属性自己赋值
this.wang = wang;
}
public String getWang() {
return wang;
}
public void setWang(String wang) {
this.wang = wang;
}
}
public class Demo {
public static void main(String[] args) {
//Animal : 姓名,年龄,颜色
//Cat :
//Dog :吼叫
//创建狗的对象
Dog d = new Dog("旺财",2,"黑色","嗷呜~~");
System.out.println(d.getName()+", " + d.getAge() + ", " + d.getColor() + ", " + d.getWang());
//创建猫的对象
Cat c = new Cat("中华田园猫",3,"黄色");
System.out.println(c.getName() + ", " + c.getAge() + ", " + c.getColor());
}
}
多态是继封装、继承之后,面向对象的第三大特性。
多态是出现在继承或者实现关系中的。
多态体现的格式:
父类类型 变量名 = new 子类/实现类构造器;
变量名.方法名();
多态的前提:
多态的好处:
调用成员变量时:编译看左边,运行看左边
调用成员方法时:编译看左边,运行看右边
代码示例:
Fu f = new Zi();
//编译看左边的父类中有没有name这个属性,没有就报错
//在实际运行的时候,把父类name属性的值打印出来
System.out.println(f.name);
//编译看左边的父类中有没有show这个方法,没有就报错
//在实际运行的时候,运行的是子类中的show方法
f.show();
父类:
public class Person {
private String name;
private int age;
空参构造
带全部参数的构造
get和set方法
public void show(){
System.out.println(name + ", " + age);
}
}
子类1:
public class Administrator extends Person {
@Override
public void show() {
System.out.println("管理员的信息为:" + getName() + ", " + getAge());
}
}
子类2:
public class Student extends Person{
@Override
public void show() {
System.out.println("学生的信息为:" + getName() + ", " + getAge());
}
}
子类3:
public class Teacher extends Person{
@Override
public void show() {
System.out.println("老师的信息为:" + getName() + ", " + getAge());
}
}
测试类:
public class Test {
public static void main(String[] args) {
//创建三个对象,并调用register方法
Student s = new Student();
s.setName("张三");
s.setAge(18);
Teacher t = new Teacher();
t.setName("王建国");
t.setAge(30);
Administrator admin = new Administrator();
admin.setName("管理员");
admin.setAge(35);
register(s);
register(t);
register(admin);
}
//这个方法既能接收老师,又能接收学生,还能接收管理员
//只能把参数写成这三个类型的父类
public static void register(Person p){
p.show();
}
}
使用格式:
子类类型 变量名 = (子类类型) 父类变量名;
如:Aniaml a = new Cat();
Cat c =(Cat) a;
为了避免ClassCastException的发生,Java提供了 instanceof
关键字,给引用变量做类型的校验,格式如下:
变量名 instanceof 数据类型
如果变量属于该数据类型或者其子类类型,返回true。
如果变量不属于该数据类型或者其子类类型,返回false。
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}
JDK14的时候提出了新特性,把判断和强转合并成了一行
//新特性
//先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d
//如果不是,则不强转,结果直接是false
if(a instanceof Dog d){
d.lookHome();
}else if(a instanceof Cat c){
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
public class Test {
public static void main(String[] args) {
//创建对象
Animal a = new Dog();
//编译看左边,运行看右边
a.eat();
//多态的弊端
//不能调用子类的特有功能
//报错的原因?
//当调用成员方法的时候,编译看左边,运行看右边
//那么在编译的时候会先检查左边的父类中有没有这个方法,如果没有直接报错。
//a.lookHome();
//解决方案:
//变回子类类型就可以了
//细节:转换的时候不能瞎转,如果转成其他类的类型,就会报错
//Cat c = (Cat) a;
//c.catchMouse();
if(a instanceof Dog){
Dog d = (Dog) a;
d.lookHome();
}else if(a instanceof Cat){
Cat c = (Cat) a;
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
// //新特性
// //先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d
// //如果不是,则不强转,结果直接是false
// if(a instanceof Dog d){
// d.lookHome();
// }else if(a instanceof Cat c){
// c.catchMouse();
// }else{
// System.out.println("没有这个类型,无法转换");
// }
}
}
class Animal{
public void eat(){
System.out.println("动物在吃东西");
}
}
class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃骨头");
}
public void lookHome(){
System.out.println("狗看家");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃小鱼干");
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
1.多态的优势
方法中,使用父类型作为参数,可以接收所有子类对象
2.多态的弊端是什么?
不能使用子类的特有功能
3.引用数据类型的类型转换,有几种方式?
自动类型转换、强制类型转换
Person p = new Student ();
student s = (student)p;
4.强制类型转换能解决什么问题?
需求:根据需求完成代码:
1.定义狗类
属性:
年龄,颜色
行为:
eat(String something)(something表示吃的东西)
看家lookHome方法(无参数)
2.定义猫类
属性:
年龄,颜色
行为:
eat(String something)方法(something表示吃的东西)
逮老鼠catchMouse方法(无参数)
3.定义Person类//饲养员
属性:
姓名,年龄
行为:
keepPet(Dog dog,String something)方法
功能:喂养宠物狗,something表示喂养的东西
行为:
keepPet(Cat cat,String something)方法
功能:喂养宠物猫,something表示喂养的东西
生成空参有参构造,set和get方法
4.定义测试类(完成以下打印效果):
keepPet(Dog dog,String somethind)方法打印内容如下:
年龄为30岁的老王养了一只黑颜色的2岁的狗
2岁的黑颜色的狗两只前腿死死的抱住骨头猛吃
keepPet(Cat cat,String somethind)方法打印内容如下:
年龄为25岁的老李养了一只灰颜色的3岁的猫
3岁的灰颜色的猫眯着眼睛侧着头吃鱼
5.思考:
1.Dog和Cat都是Animal的子类,以上案例中针对不同的动物,定义了不同的keepPet方法,过于繁琐,能否简化,并体会简化后的好处?
2.Dog和Cat虽然都是Animal的子类,但是都有其特有方法,能否想办法在keepPet中调用特有方法?
//动物类(父类)
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("动物在吃" + something);
}
}
//猫类(子类)
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("猫抓老鼠");
}
}
//狗类(子类)
public class Dog extends Animal {
public Dog() {
}
public Dog(int age, String color) {
super(age, color);
}
//行为
//eat(String something)(something表示吃的东西)
//看家lookHome方法(无参数)
@Override
public void eat(String something) {
System.out.println(getAge() + "岁的" + getColor() + "颜色的狗两只前腿死死的抱住" + something + "猛吃");
}
public void lookHome(){
System.out.println("狗在看家");
}
}
//饲养员类
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);
}
//饲养猫
public void keepPet(Cat cat, String something) {
System.out.println("年龄为" + age + "岁的" + name + "养了一只" + cat.getColor() + "颜色的" + cat.getAge() + "岁的猫");
cat.eat(something);
}*/
//想要一个方法,能接收所有的动物,包括猫,包括狗
//方法的形参:可以写这些类的父类 Animal
public void keepPet(Animal a, String something) {
if(a instanceof Dog d){
System.out.println("年龄为" + age + "岁的" + name + "养了一只" + a.getColor() + "颜色的" + a.getAge() + "岁的狗");
d.eat(something);
}else if(a instanceof Cat c){
System.out.println("年龄为" + age + "岁的" + name + "养了一只" + c.getColor() + "颜色的" + c.getAge() + "岁的猫");
c.eat(something);
}else{
System.out.println("没有这种动物");
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
//创建对象并调用方法
/* Person p1 = new Person("老王",30);
Dog d = new Dog(2,"黑");
p1.keepPet(d,"骨头");
Person p2 = new Person("老李",25);
Cat c = new Cat(3,"灰");
p2.keepPet(c,"鱼");*/
//创建饲养员的对象
Person p = new Person("老王",30);
Dog d = new Dog(2,"黑");
Cat c = new Cat(3,"灰");
p.keepPet(d,"骨头");
p.keepPet(c,"鱼");
}
}
1.抽象类的作用是什么样的?
抽取共性时,无法确定方法体,就把方法定义为抽象的。强制让子类按照某种格式重写。
抽象方法所在的类,必须是抽象类。
2.继承抽象类有哪些要注意?
3. 抽象类和抽象方法的注意事项
abstract是抽象的意思,用于修饰方法方法和类,修饰的方法是抽象方法,修饰的类是抽象类。
抽象方法
使用abstract
关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
定义格式:
修饰符 abstract 返回值类型 方法名 (参数列表);
//代码举例:
public abstract void run();
抽象类
如果一个类包含抽象方法,那么该类必须是抽象类。注意:抽象类不一定有抽象方法,但是有抽象方法的类必须定义成抽象类。
定义格式:
abstract class 类名字 {
}
//代码举例:
public abstract class Animal {
public abstract void run();
}
抽象类的使用
要求:继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。
不需要背,只要当idea报错之后,知道如何修改即可。
关于抽象类的使用,以下为语法上要注意的细节,虽然条目较多,但若理解了抽象的本质,无需死记硬背。
抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。
抽象类的子类,必须重写抽象父类中所有的抽象方法,否则子类也必须定义成抽象类,编译无法通过而报错。
理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。
抽象类存在的意义是为了被子类继承。
理解:抽象类中已经实现的是模板中确定的成员,抽象类不确定如何实现的定义成抽象方法,交给具体的子类去实现。
public abstract class Person {
private String name;
private int age;
//作用:当创建子类对象的时,给属性进行赋值的。
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//get和set方法
//抽象方法
public abstract void work();
public void sleep(){
System.out.println("睡觉");
}
}
public abstract class Teacher extends Person{
}
public class Student extends Person{
//构造方法
public Student() {
}
public Student(String name, int age) {
super(name, age); //赋值父类
}
@Override
public void work() {
System.out.println("学生的工作是学习");
}
}
public class ATeacher extends Teacher{
@Override
public void work() {
}
}
public class Test {
public static void main(String[] args) {
//创建对象
// 创建抽象类,抽象类不能创建对象
//Person p = new Person();
Student s = new Student("zhangsan",23);
System.out.println(s.getName() + ", " + s.getAge());
}
}
接口用关键字interface来定义
public interface 接口名{}
接口不能实例化(创建对象)
接口和类之间是实现关系,通过implements关键字表示
public class 类名 implements 接口名{}
**注意1:**接口和类的实现关系,可以单实现,也可以多实现。
public class 类名 implements 接口名1,接口名2{}
**注意2:**实现类还可以在继承一个类的同时实现多个接口。
public class 类名 extends 父类 implements 接口名1,接口名2{}
//接口的定义格式:
interface 接口名称{
// 抽象方法
}
// 接口的声明:interface
// 接口名称:首字母大写,满足“驼峰模式”
/**接口的实现:
在Java中接口是被实现的,实现接口的类称为实现类。
实现类的格式:*/
class 类名 implements 接口1,接口2,接口3...{
}
public interface InterF {
// 抽象方法!
// public abstract void run();
void run();
// public abstract String getName();
String getName();
// public abstract int add(int a , int b);
int add(int a , int b);
// 它的最终写法是:
// public static final int AGE = 12 ;
int AGE = 12; //常量
String SCHOOL_NAME = "黑马程序员";
}
类和类的关系
继承关系,只能单继承,不能多继承,但是可以多层继承
类和接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
接口和接口的关系
继承关系,可以单继承,也可以多继承
public interface Abc {
void go();
void test();
}
/** 法律规范:接口*/
public interface Law {
void rule();
void test();
}
*
* 总结:
* 接口与类之间是多实现的。
* 接口与接口之间是多继承的。
* */
public interface SportMan extends Law , Abc {
void run();
}
练习编写带有接口和抽象类的标准Javabean类
我们现在有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练。为了出国交流,跟乒乓球相关的人员都需要学习英语。
请用所有知识分析,在这个案例中,哪些是具体类,哪些是抽象类,哪些是接口?
乒乓球运动员:姓名,年龄,学打乒乓球,说英语
篮球运动员:姓名,年龄,学打篮球
乒乓球教练:姓名,年龄,教打乒乓球,说英语
篮球教练:姓名,年龄,教打篮球
方法一:
方法二:我们采用的
代码复现:
父类:
//因为现在我不想让外界去直接创建人的对象
//因为直接创建顶层父类人的对象此时是没有意义的
//所以我就把他写为抽象的。
public abstract class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//set和get方法
}
第二层
接口:
public interface English {
public abstract void speakEnglish();
//如果implement这个接口后,接口中的方法必须全部重载,要不然或报错
//public void speak1(); //=public abstract void speak1();
}
javabean类
//运动员类
public abstract class Sporter extends Person{
public Sporter() {
}
public Sporter(String name, int age) {
super(name, age);
}
public abstract void study();
}
//教练类
public abstract class Coach extends Person{
public Coach() {
}
public Coach(String name, int age) {
super(name, age);
}
public abstract void teach();
}
//乒乓球运动员
public class PingPangSporter extends Sporter implements English{
public PingPangSporter() {
}
public PingPangSporter(String name, int age) {
super(name, age);
}
@Override
public void speakEnglish() {
System.out.println("乒乓球运动员在说英语");
}
@Override
public void study() {
System.out.println("乒乓球运动员在学习如何打乒乓球");
}
}
//乒乓球教练
public class PingPangCoach extends Coach implements English{
public PingPangCoach() {
}
public PingPangCoach(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("乒乓球教练正在教如何打乒乓球");
}
@Override
public void speakEnglish() {
System.out.println("乒乓球教练在学习说英语");
}
}
//
public class BasketballCoach extends Coach{
public BasketballCoach() {
}
public BasketballCoach(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("篮球教练正在教如何打篮球");
}
}
//
public class BasketballSporter extends Sporter{
public BasketballSporter() {
}
public BasketballSporter(String name, int age) {
super(name, age);
}
@Override
public void study() {
System.out.println("篮球运动员在学习如何打篮球");
}
}
测试:
public class Test {
public static void main(String[] args) {
//创建运动员或教练的对象
PingPangSporter pps = new PingPangSporter("刘诗雯",23);
System.out.println(pps.getName() + ", " + pps.getAge());
pps.study();
pps.speakEnglish();
}
}
允许在接口中定义默认方法,需要使用关键字default修饰
作用:解决接口升级的问题
接口中默认方法的定义格式:
格式: public default 返回值类型 方法名(参数列表){}
范例: public default void show(){ }
//接口A
public interface InterA {
public abstract void method();
public default void show(){
System.out.println("InterA接口中的默认方法 ---- show");
}
}
//javabean类
public class InterImpl implements InterA{
@Override
public void method() {
System.out.println("实现类重写的抽象方法");
}
}
//实现类
public class Test {
public static void main(String[] args) {
//创建实现类的对象
InterImpl ii = new InterImpl();
ii.method();
ii.show();
}
}
//输出
实现类重写的抽象方法
InterA接口中的默认方法 ---- show
//接口A
public interface InterA {
public abstract void method();
public default void show(){
System.out.println("InterA接口中的默认方法 ---- show");
}
}
//接口b
public interface InterB {
public default void show(){
System.out.println("InterB接口中的默认方法 ---- show");
}
}
//javabean类
public class InterImpl implements InterA{
@Override
public void method() {
System.out.println("实现类重写的抽象方法");
}
//重写的时候去掉default关键字
@Override
public void show() {
System.out.println("重写接口中的默认方法");
}
}
//实现:测试
public class Test {
public static void main(String[] args) {
/*接口中默认方法的定义格式:
格式:public default 返回值类型 方法名(参数列表) { }
接口中默认方法的注意事项:
1.默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字
2.public可以省略,default不能省略
3.如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
*/
//创建实现类的对象
InterImpl ii = new InterImpl();
ii.method();
ii.show();
}
}
//输出
实现类重写的抽象方法
重写接口中的默认方法
允许在接口中定义静态方法,需要使用关键字static修饰
接口中静态方法的定义格式:
格式: public static 返回值类型 方法名(参数列表){}
范例: public static void show(){ }
案例
//接口
public interface Inter {
public abstract void method();
public static void show(){
System.out.println("Inter接口中的静态方法");
}
}
//javabean类
public class InterImpl implements Inter{
@Override
public void method() {
System.out.println("InterImpl重写的抽象方法");
}
//不叫重写
public static void show() {
System.out.println("InterImpl重写的抽象方法");
}
}
public class Test {
public static void main(String[] args) {
//调用接口中的静态方法
Inter.show();
//调用实现类中的静态方法
InterImpl.show();
//子类把从父类继承下来的虚方法表里面的方法进行覆盖了,这才叫重写。
}
}
//输出
Inter接口中的静态方法
InterImpl重写的抽象方法
public class Test {
public static void main(String[] args) {
/* 接口中私有方法的定义格式:
格式1:private 返回值类型 方法名(参数列表) { }
范例1:private void show() { }
格式2:private static 返回值类型 方法名(参数列表) { }
范例2:private static void method() { }
*/
}
}
public interface InterA {
public static void show1(){
System.out.println("show1方法开始执行了");
show4();
}
public static void show2(){
System.out.println("show2方法开始执行了");
show4();
}
//普通的私有方法,给默认方法服务的
private void show3(){
System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
}
//静态的私有方法,给静态方法服务的
private static void show4(){
System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
}
}
1.接口代表规则,是行为的抽象。想要让哪个类拥有一个行为,就让这个类实现对应的接口就可以了。
2.当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态。
1.当一个接口中抽象方法过多,但是我只要使用其中一部分的时候,就可以适配器设计模式
2.书写步骤:
//接口
public interface Inter {
public abstract void method1();
public abstract void method2();
public abstract void method3();
public abstract void method4();
public abstract void method5();
public abstract void method6();
public abstract void method7();
public abstract void method8();
public abstract void method9();
public abstract void method10();
}
//中间类
public abstract class InterAdapter implements Inter{
@Override
public void method1() {
}
@Override
public void method2() {
}
@Override
public void method3() {
}
@Override
public void method4() {
}
@Override
public void method5() {
}
@Override
public void method6() {
}
@Override
public void method7() {
}
@Override
public void method8() {
}
@Override
public void method9() {
}
@Override
public void method10() {
}
}
实现类
public class InterImpl extends InterAdapter{
//我需要用到那个方法,就重写哪个方法就可以了
@Override
public void method5() {
System.out.println("只要用第五个方法");
}
}
**类的五大成员:**属性、方法、构造方法、代码块、内部类
public class Car {
String carName;
int carAge;
String carColor;
public void show(Car this){
//是打印调用者车的名字:宾利
System.out.println(this.carName);
//调用内部类
Engine e = new Engine();
System.out.println(e.engineName);
}
//内部类
class Engine{
String engineName;
int engineAge;
public void show(){
System.out.println(engineName);
System.out.println(carName);
}
}
}
按定义的位置来分
1.成员内部类的代码如何书写
2.如何创建成员内部类的对象
3.成员内部类如何获取外部类的成员变量
成员内部类:
写在成员位置的,属于外部类的成员。
成员内部类可以被一些修饰符所修饰,比如: private,默认,protected,public,static等在成员内部类里面,
JDK16之前不能定义静态变量,JDK 16开始才可以定义静态变量。
public class car {外部类
string carName;
int carAge;
int carcolor;
//成员内部类
class Engine{
string engineName;
int engineAge;
}
}
成员内部类特点:
内部类的使用格式:
外部类.内部类。 // 访问内部类的类型都是用 外部类.内部类
获取成员内部类对象的两种方式:
方式一:外部直接创建成员内部类的对象
外部类.内部类 变量 = new 外部类().new 内部类();
方式二:在外部类中定义一个方法提供内部类的对象
方式一:
public class Test {
public static void main(String[] args) {
// 宿主:外部类对象。
// Outer out = new Outer();
// 创建内部类对象。
Outer.Inner oi = new Outer().new Inner();
oi.method();
}
}
class Outer {
// 成员内部类,属于外部类对象的。
// 拓展:成员内部类不能定义静态成员。
public class Inner{
// 这里面的东西与类是完全一样的。
public void method(){
System.out.println("内部类中的方法被调用了");
}
}
}
方式二:
public class Outer {
String name;
private class Inner{
static int a = 10;
}
public Inner getInstance(){
return new Inner();
}
}
public class Test {
public static void main(String[] args) {
Outer o = new Outer();
System.out.println(o.getInstance());
}
}
成员内部类的细节
编写成员内部类的注意点:
详解:
内部类被private修饰,外界无法直接获取内部类的对象,只能通过3.3节中的方式二获取内部类的对象
被其他权限修饰符修饰的内部类一般用3.3节中的方式一直接获取内部类的对象
内部类被static修饰是成员内部类中的特殊情况,叫做静态内部类下面单独学习。
内部类如果想要访问外部类的成员变量,外部类的变量必须用final修饰,JDK8以前必须手动写final,JDK8之后不需要手动写,JDK默认加上
请在?地方向上相应代码,以达到输出的内容
注意:内部类访问外部类对象的格式是:外部类名.this
public class Test {
public static void main(String[] args) {
Outer.inner oi = new Outer().new inner();
oi.method();
}
}
class Outer { // 外部类
private int a = 30;
// 在成员位置定义一个类
class inner {
private int a = 20;
public void method() {
int a = 10;
System.out.println(???); // 10 答案:a
System.out.println(???); // 20 答案:this.a
System.out.println(???); // 30 答案:Outer.this.a
}
}
}
静态内部类特点:
静态内部类是一种特殊的成员内部类。
有static修饰,属于外部类本身的。
总结:静态内部类与其他类的用法完全一样。只是访问的时候需要加上外部类.内部类。
拓展1:静态内部类可以直接访问外部类的静态成员。
拓展2:静态内部类不可以直接访问外部类的非静态成员,如果要访问需要创建外部类的对象。
拓展3:静态内部类中没有银行的Outer.this。
内部类的使用格式:
外部类.内部类。
静态内部类对象的创建格式:
外部类.内部类 变量 = new 外部类.内部类构造器;
调用方法的格式:
案例演示:
// 外部类:Outer01
class Outer01{
private static String sc_name = "黑马程序";
// 内部类: Inner01
public static class Inner01{
// 这里面的东西与类是完全一样的。
private String name;
public Inner01(String name) {
this.name = name;
}
public void showName(){
System.out.println(this.name);
// 拓展:静态内部类可以直接访问外部类的静态成员。
System.out.println(sc_name);
}
}
}
public class InnerClassDemo01 {
public static void main(String[] args) {
// 创建静态内部类对象。
// 外部类.内部类 变量 = new 外部类.内部类构造器;
Outer01.Inner01 in = new Outer01.Inner01("张三");
in.showName();
}
}
局部内部类
1.将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。
2.外界是无法直接使用局部内部类,需要在方法内部创建对象并使用。
3.该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
定义格式:
class 外部类名 {
数据类型 变量名;
修饰符 返回值类型 方法名(参数列表) {
// …
class 内部类 {
// 成员变量
// 成员方法
}
}
}
public class Outer {
int b = 20;
public void show(){
int a = 10;
//局部内部类
class Inner{
String name;
int age;
public void method1(){
System.out.println(a);
System.out.println(b);
System.out.println("局部内部类中的method1方法");
}
public static void method2(){
System.out.println("局部内部类中的method2静态方法");
}
}
//创建局部内部类的对象
Inner i = new Inner();
System.out.println(i.name);
System.out.println(i.age);
i.method1();
Inner.method2();
}
}
public class Test {
public static void main(String[] args) {
/*
局部内部类
1.将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。
2.外界是无法直接使用局部内部类,需要在方法内部创建对象并使用。
3.该类可以直接访问外部类的成员,也可以访问方法内的局部变量。*/
//调用show方法,让代码执行
Outer o = new Outer();
o.show();
}
}
1.什么是匿名内部类?
隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置。
2.格式的细节
包含了继承或实现,方法重写,创建对象。
整体就是一个类的子类对象或者接口的实现类对象
3.使用场景
当方法的参数是接口或者类时,
以接口为例,可以传递这个接口的实现类对象,
如果实现类只要使用一次,就可以用匿名内部类简化代码。
//接口
public interface Swim {
public abstract void swimming();
}
//实现1
public class Demo07 {
public static void main(String[] args) {
new Swim() {
@Override
public void swimming() {
System.out.println("自由泳...");
}
}.swimming();
}
}
//输出
自由泳...
//实现2
public class Demo07 {
public static void main(String[] args) {
// 接口 变量 = new 实现类(); // 多态,走子类的重写方法
Swim s2 = new Swim() {
@Override
public void swimming() {
System.out.println("蛙泳...");
}
};
s2.swimming();
}
}
//输出
蛙泳...
interface Swim {
public abstract void swimming();
}
public class Demo07 {
public static void main(String[] args) {
// 普通方式传入对象
// 创建实现类对象
Student s = new Student();
goSwimming(s);
// 匿名内部类使用场景:作为方法参数传递
Swim s3 = new Swim() {
@Override
public void swimming() {
System.out.println("蝶泳...");
}
};
// 传入匿名内部类
goSwimming(s3);
// 完美方案: 一步到位
goSwimming(new Swim() {
public void swimming() {
System.out.println("大学生, 蛙泳...");
}
});
goSwimming(new Swim() {
public void swimming() {
System.out.println("小学生, 自由泳...");
}
});
}
// 定义一个方法,模拟请一些人去游泳
public static void goSwimming(Swim s) {
s.swimming();
}
}