在下面的 Java 代码中,定义了一个名为 Person
的类,并提供了构造方法来初始化对象的属性。类中定义了 eat
、sleep
和 work
三个方法,用于表示人的行为。在 main
方法中,创建了一个名为 p1
的 Person
对象,并调用了其方法来展示人的行为。
通过这样的代码组织,我们可以创建多个不同的 Person
对象,每个对象都有自己独立的属性和行为。
public class Person {
//属性
private String name;
private int age;
private String gender;
public Person(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
//方法,也就是行为
public void eat() {
System.out.println(name + " is eating.");
}
public void sleep() {
System.out.println(name + " is sleeping.");
}
public void work() {
System.out.println(name + " is working.");
}
public static void main(String[] args) {
//实例化了一个p1的对象
Person p1 = new Person("Tom", 25, "male");
p1.eat();
p1.sleep();
p1.work();
}
}
1)在java中,类只支持单继承,不允许多重继承,也就是说,一个类只能有一个直接的父亲,例如下面这种情况是不合法的
class A{}
class B{}
class C extends A,B
//这种写法是错误的
2)但是多个类可以同时继承一个父亲
class A{}
class B extends A{}
class C extends A{}
3)多层继承也是可以的
这时候C也可以被称作A的子类
class A{}
class B extends A{}
class C extends B{}
class Animal{
private String name;
private int 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;
}
}
就近原则
特点:
1.可以通过类名直接访问
2.静态变量是随着类的加载而加载,是优先于对象出现的
静态变量被所有对象共享,什么意思?
下面是例子,
展示了静态变量可以通过类名访问,且被所有对象共享
调用静态变量的方法
这里引入了一个新的概念——工具类,借此来整合一下目前遇到的所有类
工具类:帮助我们做一些事情,但是不描述任何事物的类。
特点:
1.见类名知其作用
2.私有化构造方法(使该类无法被示例化)
3.方法定义为静态的(不用实例化对象,也可以调用方法)
举例,计算器的类就可以被看作是工具类,因为不需要实例化对象,只需要调用方法就够了
public class ArrayUtil {
//私有化构造方法,为了不让外界实例化对象
private ArrayUtil() {};
//定义为静态方法,方便调用
public static void 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]);
sb.append(",");
}
}
sb.append("]");
System.out.println(sb.toString());
}
public static double getAverge(int[] arr) {
double sum =0;
for (int i = 0; i < arr.length; i++) {
sum+=arr[i];
}
return sum/arr.length;
}
}
public class testCalc {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
ArrayUtil.printArr(arr);
double ret = ArrayUtil.getAverge(arr);
System.out.println(ret);
}
}
如果一个类没有父类,则默认继承于object
可以看到,下层子类拥有上层父类的特点
总结:
1.构造方法一律不可以被继承的
2.成员变量一律可以被继承。(但是子类虽然能继承父类的private,却不能访问)
3.成员方法非私有可以被继承,私有不能被继承
在子类中,只允许直接访问父类中非private的属性和方法,换句话说,父类中private修饰的属性和方法在子类中无法直接访问,但是可以用getter和setter方法间接访问
class Animal{//父类
private int age;
public int getAge(){
return age;
}
String name;
}
class Dog extends Animal{//子类extends(继承)父类
String color;
void print(){
System.out.println(age);//直接访问父类中的private属性,错误!
System.out.println(name);//父类中的name为default权限,可以访问
}
}
子类已经拥有父类的所有属性和方法,扩充子类的属性和方法,让子类描述的对象更加具体
如下。Dog类示例化的对象首先会拥有Animal类的两个属性,并在此基础上扩充了一个颜色属性
class Animal{//这是父类
int age;
String name;
}
class Dog extends Animal{//子类extends(继承)父类
String color;
}
为了满足不同需求,子类可以对父类的方法重写。
public class Main {
public static void main(String[] args) {
Dog d = new Dog();
d.shout();
}
}
class Animal{//这是父类
int age;
String name;
void shout(){
System.out.println("发出了奇怪的叫声");
}
}
class Dog extends Animal{//子类extends(继承)父类
String color;
void shout(){//重写
System.out.println("发出了汪汪汪的叫声");
}
}
注意,子类中重写方法的权限不得低于父类中原方法的权限
class Animal{//这是父类
int age;
String name;
void shout(){
System.out.println("发出了奇怪的叫声");
}
}
class Dog extends Animal{//子类extends(继承)父类
String color;
private void shout(){//这样重写就错了
System.out.println("发出了汪汪汪的叫声");
}
}
如果A类想调用方法C,是在A类找方法C,找不到去B类找,然后再去C类找,是这样吗?
不是的,因为效率太低了,于是有了虚方法表。
1.只有当两种类型有继承关系时,才能对象类型转换
2.向上类型转换。即子类转换成父类,这说无需强制类型转换的,因为父类有的东西子类肯定也有。转换完后,只能使用父类中的方法。因为已经变成父类的类型了,自然不能调用子类的方法。语法格式如下
fatherClass obj = new sonClass();
3.向下类型转换(强制类型转换)。这个操作有约束条件的。向下转型通常发生在父类类型的引用指向子类对象的情况下。确保在进行向下转型之前,引用确实指向了正确的子类对象,否则可能会出现运行时异常。
sonClass obj = (sonClass) fatherClass;
interface Animal{
void eat();
void sleep();
}
interface Pet extends Animal{
void play();
void bark();
}
class Teddy implements Pet{
public void bark() {
System.out.println("汪汪汪");
}
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("吃骨头");
}
@Override
public void sleep() {
// TODO Auto-generated method stub
System.out.println("睡懒觉");
}
@Override
public void play() {
// TODO Auto-generated method stub
System.out.println("和主人玩");
}
}
public class testCalc {
public static void main(String[] args) {
//创造Teddy对象并向上转型(无需强制类型转换)
Pet teddy = new Teddy();
teddy.eat();teddy.sleep();
teddy.play();
//teddy.bark();
//这样就不行了,因为teddy现在是Pet类型,而bark方法是Teddy类里的
//向下转型(强制类型转换)
if(teddy instanceof Teddy) {
//如果teddy此时的类型确实是Teddy类的父类
//那么可以进行向下转型
teddy = (Teddy)teddy;
teddy.bark();
}
}
}
例如我们定义了一个Person类,里面的work方法是空着的(或者随便写一个,就像图中一样),这是因为,不同的人的工作是不同的,无法统一,因此无法在Person类里把work方法写好,换言之,有不确定的方法体。
需要依靠对象进行重写。
当我们去实例化老师和学生这两个对象的时候,student和teacher两个对象需要对work方法进行重写。但倘若忘记重写或没有重写,就不合适了。为了防止忘记重写的情况发生,我们要用上抽象类来“逼”对象进行重写,怎么个“逼”法?往下看。
像这样定义了一个抽象类,类中的抽象方法,子类必须强制进行重写,否则会报错。
注意!有抽象方法所在的类必须定义成抽象类!
public abstract class AbstractClass {//抽象类
// 抽象方法
//需要注意的是,抽象方法括号()完直接加封号;
public abstract void abstractMethod();
// 普通方法
public void concreteMethod() {
// 方法体
}
}
当子类继承了抽象类之后,需要重写抽象方法。重写抽象方法的格式如下:
public class ConcreteClass extends AbstractClass {
@Override
public void abstractMethod() {
// 实现抽象方法的具体逻辑
}
}
1.抽象类不能实例化
2.抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
3.抽象类可以有构造方法(可以有疑惑,既然不能实例化为什么还要有构造方法呢?因为构造方法可以用于初始化成员变量)
4.抽象类的子类,要么重写抽象类中所有的抽象方法,要么这个子类也是个抽象类
多继承:Java中的类只支持单一继承,但是通过接口,一个类可以实现多个接口,从而获得了类似于多继承的特性。这使得Java中的类能够更加灵活地组合和复用代码。同时,避免了多继承所带来的复杂性和歧义性。
解耦:接口可以将抽象和实现分离,从而降低了代码之间的耦合度。使用接口能够让程序更容易扩展和维护,因为实现类只需关注接口定义的方法即可,而不需要关心具体的实现细节。
规范约束:接口定义了一组行为规范,使得类能够按照这些规范进行设计和实现。这有助于提高代码的可读性、可维护性和可扩展性。
多态性:接口与多态性密切相关,接口的引用变量可以指向实现了该接口的任何类的对象,从而实现了对不同类的统一处理。
举个例子,现在有个游泳的功能,写到动物类里是不合适的。因为不是所有动物都会游泳,所以可以把游泳功能写成一个接口,单独提供给能游泳的动物。
abstract class Animal{
//方法有抽象类,那么整个类也要变成抽象类
private String name;
private int age;
public Animal() {
super();
// TODO Auto-generated constructor stub
}
public Animal(String name, int age) {
super();
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();
//因为每个动物都有自己吃的东西,无法统一行为
//干脆不写,直接把eat方法修饰为抽象类
}
//因为总共有兔子,青蛙,狗三种动物
//兔子不会游泳,所以不能把swim行为加在Animal类里
//但是如果在青蛙和狗的类里单独加Swim,可能会导致不统一
//因为青蛙里的可能叫swim,狗里的叫swimming,这就不统一了
//所以要定义一个swim接口,谁会游泳谁就继承,然后在自己的类里重写即可
interface Swim{
public void Swim();
}
class Frog extends Animal implements Swim{
@Override
public void Swim() {
System.out.println("蛙泳");
}
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("吃蚊子");
}
}
class Dog extends Animal implements Swim{
@Override
public void Swim() {
System.out.println("狗刨式");
}
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("吃骨头");
}
}
class Rabbit extends Animal{
@Override
public void eat() {
System.out.println("吃胡萝卜");
}
public Rabbit() {
super();
// TODO Auto-generated constructor stub
}
public Rabbit(String name, int age) {
super(name, age);
// TODO Auto-generated constructor stub
}
}
抽象方法:接口中的方法都是抽象方法,不包含具体的实现代码;
多继承:Java中的类可以实现多个接口,这使得Java支持了接口的多继承性质;
关键字:在Java中,使用interface
关键字来声明一个接口;
实现:类通过使用implements
关键字来实现一个或多个接口,实现接口的类必须提供接口中定义的所有方法的具体实现;(有点像继承抽象类)
接口之间的继承:接口可以继承其他接口(但并不实现,因为接口是不包含具体的实现代码),使用extends
关键字;
常量:接口中可以包含常量,这些常量默认为public static final
类型;
方法实现:接口中的方法都是抽象的,没有方法体;而抽象类中可以包含抽象方法,也可以包含具体的方法实现。
多继承:在Java中,一个类只能继承一个抽象类,但可以实现多个接口。这意味着接口支持多继承,而抽象类不支持。
构造方法:抽象类可以有构造方法,而接口不能有构造方法。
成员变量:抽象类中可以包含成员变量,而接口中的变量默认为public static final
类型的常量。
设计目的:抽象类用于表示一种“is-a”关系,即子类是父类的一种特殊类型;而接口更多地用于表示一种“has-a”关系,即类具有某种特定的行为能力。
综上所述,抽象类和接口在某些方面确实有相似之处,但它们的设计目的和使用场景有所不同。一般来说,如果需要表示一种层次结构,并且希望共享代码或者包含成员变量,可以使用抽象类;而如果需要表示一种行为规范,并且希望实现多重继承,可以使用接口。
例题
写之前要先画图,提取共同特征到父类,特殊行为用接口实现
以下的思路对吗?是对的,但不够简洁规范。
因为父类的所有子类都应该是同一种事物,但教练和运动员不是同一种事物,因此可以再分类
修改过后,简洁很多
//因为创建人的对象是没有意义的,
//为了不让外界实例化人,修饰成抽象类
abstract class Person{
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Person() {}
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;
};
}
abstract class player extends Person{
public player() {
super();
// TODO Auto-generated constructor stub
}
public player(String name, int age) {
super(name, age);
// TODO Auto-generated constructor stub
}
public abstract void learn();
//public abstract void game();
}
abstract class coach extends Person{
public coach() {
super();
// TODO Auto-generated constructor stub
}
public coach(String name, int age) {
super(name, age);
// TODO Auto-generated constructor stub
}
public abstract void teach();
}
interface speak{
public abstract void speadEndlish();
}
class pingpong_Player extends player implements speak{
public pingpong_Player() {
super();
// TODO Auto-generated constructor stub
}
public pingpong_Player(String name, int age) {
super(name, age);
// TODO Auto-generated constructor stub
}
@Override
public void speadEndlish() {
// TODO Auto-generated method stub
System.out.println("会讲英语");
}
@Override
public void learn() {
// TODO Auto-generated method stub
System.out.println("学打乒乓球");
}
}
class basketball_Player extends player{
public basketball_Player() {
super();
// TODO Auto-generated constructor stub
}
public basketball_Player(String name, int age) {
super(name, age);
// TODO Auto-generated constructor stub
}
@Override
public void learn() {
// TODO Auto-generated method stub
System.out.println("学打篮球");
}
}
class pingpong_Coach extends coach implements speak{
public pingpong_Coach() {
super();
// TODO Auto-generated constructor stub
}
public pingpong_Coach(String name, int age) {
super(name, age);
// TODO Auto-generated constructor stub
}
@Override
public void teach() {
// TODO Auto-generated method stub
System.out.println("教打乒乓球");
}
@Override
public void speadEndlish() {
// TODO Auto-generated method stub
System.out.println("会讲英语");
}
}
class basketball_Coach extends coach{
public basketball_Coach() {
super();
// TODO Auto-generated constructor stub
}
public basketball_Coach(String name, int age) {
super(name, age);
// TODO Auto-generated constructor stub
}
@Override
public void teach() {
// TODO Auto-generated method stub
System.out.println("教打篮球");
}
}
public class testCalc {
public static void main(String[] args) {
pingpong_Player p1 = new pingpong_Player("马龙",18);
System.out.println(p1.getName()+","+p1.getAge());
p1.learn();p1.speadEndlish();
System.out.println("-------------------");
pingpong_Coach p2 = new pingpong_Coach("刘国强",55);
System.out.println(p2.getName()+","+p2.getAge());
p2.teach();p2.speadEndlish();
System.out.println("-------------------");
basketball_Player b1 = new basketball_Player("姚明",20);
System.out.println(b1.getName()+","+b1.getAge());
b1.learn();
System.out.println("-------------------");
basketball_Coach b2 = new basketball_Coach("姚明的教练",50);
System.out.println(b2.getName()+","+b2.getAge());
b2.teach();
System.out.println("-------------------");
}
}