1.抽象类
1.1抽象类的概念
1.abstract修饰的方法,叫做抽象方法,是没有具体实现的。(可以理解为我描述的是动物这个大类而不是描述动物这个类中的具体的对象例如狗)
2.如果一个类,包含了这个抽象方法,此时这个类必须得用abstract修饰,此时这个类被叫做
3.当我们定义一个类我们不能详细的描述的个类
抽象类
代码
abstract class shap {//抽象类
public abstract void draw();//抽象方法
}
public class Frank{
}
3.抽象类中可以有和普通类中的成员变量和成员放法。
4.抽象类是不能够实例化的!只能用普通类进行继承。
5.当一个普通类继承了这个抽象类,这个普通类必须要重写抽象方法。
2.如何使用抽象类?
1.我们通过定义一个shap的抽象类,再定一个Rect类这个类作为shap的子类继承它,一个重要的一点是继承了抽象类的子类要对抽象类中的抽象方法进行重写。
代码:
abstract class Shap {
public abstract void draw();
}
class Rect extends Shap{
@Override
public void draw(){
System.out.println("画矩形");
}
}
public class Frank{
public static void main(String[] args) {
}
}
2.通过抽象类实现继承
abstract class Shape {
public abstract void draw();
}
class Rect extends Shape{
@Override
public void draw(){
System.out.println("画矩形");
}
}
class Cycle extends Shape{
@Override
public void draw(){
System.out.println("画圆圈");
}
}
class Flower extends Shape{
@Override
public void draw(){
System.out.println("花");
}
}
public class Frank{
public static void func(Shape shape){
shape.draw();
}
public static void main(String[] args) {
func(new Flower());
func(new Cycle());
func(new Rect());
}
}
3.注意事项
(1)在main方法中只能调用静态方法因为main也是被static修饰
静态不能访问实例,除非实例化对象,然后通过实例化对象来引用!!
(2)抽象方法是不能加final 和 private 和static 因为抽象类就是为了重写的 。
(3)抽象类必须要被继承,而且子类一定要重写抽象类中方法。
(4)如果一个类不想实现抽象类的方法我们可以把我不想重写可以把这个类加一个abstract。
(5)抽象类中可以有普通类中的成员方法和成员变量
(6)抽象类中可以没有抽象方法,但是只要这个类中有抽象方法那么这个类一定是抽象类。
2.1抽象类中的构造方法
1.我们将抽象类中定义两个类型的成员变量,name和age,再写出它的构造方法,这时候就有一个问题,我们不能够实例化抽象类,那么我们写这个构造方法的目的是什么?
abstract class person {
public String name;
public int age ;
public person(String name, int age) {
this.name = name;
this.age = age;
}
}
2.构造方法的目的。对父类也就是student这个类的父类person这个抽象类中的成员变量进行初始化。
abstract class person {
public String name;
public int age ;
public person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Student extends person {
public Student(String name, int age) {
super(name, age);
}
}
3.总结
(1)抽象类中可以包含构造方法,这个构造方法并不是再实例化这个抽象类的时候使用因为它就不能被实例化。那么这个构造方法主要是在子类中让子类调用,帮助父类进行初始化。
3.抽象类的作用
3.1.抽象类特性
抽象类本身不能进行实例化,要想使用只能创建该抽象类的子类,然后让子类重写抽象类中的方法。
3.2.引出问题
(问题)抽象类可以继承普通类也可以继承,抽象方法可以重写,普通类中的方法也可以重写,那么抽象类和普通类的区别是什么呢??
(1)抽象类相当于多了编译器的提醒,提醒你的方法是不是已经重写,我普通子类在继承完父类然后重写方法如果我没有重写编译器是不会报错的,抽象类被子类继承没有重写抽象方法是会报错的!
3.3抽象类和普通图标区别
红色圆圈圈起来的就是抽象类的标志,像一个篮球🏀
4.接口
4.1接口的概念
1.现实生活中,接口的例子比比皆是,比如:笔记本上的USB口,电源插座等等。(一种规定的标准)
2.接口就是公共行为规范标准,大家在实现时,只要符合规范标准就可以通用。
(在java中,接口可以看成是:多个类的公共规范,是一种引用数据类型)
3.抽象类是抽象的那么接口就是抽象的抽象!
4.2接口的实现
4.2.1接口的简单实现
1.建一个包用来放接口,名称是demo2
2.下面图片看到shap抽象类中没有非抽象方法。
3.我们定义一个接口,改造一个方法(定义接口的规则:interface+接口的名字)
这时候demo文件夹下就会出现接口如下图
4.2.1接口实现详细讲解
4.2.1.1定义一个接口
1.接口中可以定义成员变量
2.接口中只能使用抽象方法要不然就会报错!
3.要想在接口中使用普通的方法必须要加default(jdk8新特性)修饰或者static修饰否则其他情况就必须要是抽象方法!!!
4.接口中定义的成员变量全部都是由public static final 修饰的!
5.我们将接口中的成员变量定义成下面几种方式都是没有报错的,所以在java中,接口的成员变量默认的修饰是public static final。
package demo2;
interface IsShape{
public int a = 10 ;
public int b = 11 ;
public static int aaa = 10 ;
public static final int bbb = 10 ;
}
建议平时在接口中写成员变量 和抽象方法这样写如下图。
6.接口中的抽象方法默认都是public static 修饰的。
7.接口是不可以被实例化的!
8.接口的运行完后会生成一共字节码文件结尾是.java。
9.一个类可以继承一个普通类或者抽象类还可以实现一个接口。(我们必须先继承再实现格式是这个)
代码如下
class cc extends Keybord implements IUSB{
}
4.2.1.2接口的使用
1.既然我们不能对接口进行实例化我们该如何使用接口?
4.2.1.2.1 implements关键字
1.类和接口的关系使用implements来进行练习(中文意思实现)
2.下面代码会使用接口,我定义了两个类来实现接口,并且对接口中draw这个抽象方法进行重写。
package demo2;
interface IsShape{
int c = 100;
void draw();
// public int a = 10 ;
// public int b = 11 ;
// public static int aaa = 10 ;
// public static final int bbb = 10 ;
// public default void test_2(){
// System.out.println("default");
// }
// public static void text_3(){
// System.out.println("static");
// }
}
class Rect implements IsShape{
@Override
public void draw(){
System.out.println("画矩形");
}
}
class Cycle implements IsShape{
@Override
public void draw(){
System.out.println("画圆圈");
}
}
public class Test {
public static void main(String[] args) {
Cycle cycle = new Cycle();
Rect rect = new Rect();
cycle.draw();
rect.draw();
}
}
3.
我们将抽象类通过继承来实现和上面接口实现类似的代码,进行展现.
我们可以看到接口和抽象类不是一样的吗?那我们为什么要用接口?
abstract class Shape {
public abstract void draw();
}
class Rect extends Shape{
@Override
public void draw(){
System.out.println("画矩形");
}
}
class Cycle extends Shape{
@Override
public void draw(){
System.out.println("画圆圈");
}
}
public static void main(String[] args) {
func(new Cycle());
func(new Rect());
}
}
4.接口实现多态
(1).向上转型然后重写抽象方法实现多态
(2).这里并没有实例化IsShap,而是向上转型希望大家注意。
(3)发生了动态绑定
package demo2;
interface IsShape{
int c = 100;
void draw();
// public int a = 10 ;
// public int b = 11 ;
// public static int aaa = 10 ;
// public static final int bbb = 10 ;
// public default void test_2(){
// System.out.println("default");
// }
// public static void text_3(){
// System.out.println("static");
// }
}
class Rect implements IsShape{
@Override
public void draw(){
System.out.println("画矩形");
}
}
class Cycle implements IsShape{
@Override
public void draw(){
System.out.println("画圆圈");
}
}
class Flower implements IsShape {
@Override
public void draw(){
System.out.println("花");
}
}
public class Test {
public static void main(String[] args) {
// Cycle cycle = new Cycle();
// Rect rect = new Rect();
// cycle.draw();
// rect.draw();
IsShape isShape_1 = new Cycle();
IsShape isShape_2 = new Rect();
isShape_1.draw();
isShape_2.draw();
}
}
5.
到目前为止我们还没有看出接口真正的用处是什么,我们可以发现目前的接口使用方式没啥特别的,在接下来我会给大家进行讲解让大家了解接口的真正作用!!!!
4.2.1.3接口详细讲解
4.2.1.3.1.新建接口
2.在我们新建接口的时候接口会生成一个字节码文件
4.2.1.3.2.接口的使用
1.注意事项
(1)创建接口时,接口的命名以大写字母I开头。
(2)接口的命名一般使用“形容词”词性的单词
(3)阿里编码规范中约定,接口中的方法和属性不要加仍和修饰符号,保持代码的简洁性。
2.一个类中后者能给我们要有一个实现类implement来实现一个接口
3.例子,代码如下
(1).在java中intanceof是用来判断这个子类是否是这个类的父类的,如果是就输出true 不是就输出false(在下面代码中会用到)
(2)代码很多我会以每个包有哪些代码进行展示。
一共这几个包
Computer类代码
package demo2;
public class Computer implements Comparable {
public void poweron(){
System.out.println("打开电脑!");
}
public void poweroff(){
System.out.println("关闭电脑!");
}
public void useDeive(IUSB iusb){
iusb.openDeice();
iusb.closeDevice();
if(iusb instanceof Mouse){
Mouse mouse = (Mouse) iusb;
}else if(iusb instanceof Keybord){
Keybord keybord = (Keybord) iusb;
}else{
}
}
@Override
public int compareTo(Object o) {
return 0;
}
}
IUSB接口代码
package demo2;
public interface IUSB {
void openDeice();
void closeDevice();
}
Keyboard类代码
package demo2;
public class Keybord implements IUSB{
@Override
public void openDeice() {
System.out.println("打开键盘!");
}
public void inPut(){
System.out.println("关闭键盘!");
}
@Override
public void closeDevice() {
System.out.println("关闭键盘");
}
}
Mouse类代码
package demo2;
public class Mouse implements IUSB {
@Override
public void openDeice() {
System.out.println("打开鼠标");
}
public void click(){
System.out.println("打开鼠标!");
}
@Override
public void closeDevice() {
System.out.println("关闭鼠标!");
}
}
Test类main方法代码
package demo2;
public class Test {
public static void main(String[] args) {
Computer computer = new Computer();
computer.poweron();
computer.useDeive(new Mouse());
computer.useDeive(new Keybord());
computer.poweroff();
}
}
(4)结果完成电脑开关机的基本动作
目前位置还没有体现出接口与抽象类的区别,这个案例我用抽象类也可以完全的书写出来。
4.2.1.3.3 实现多个接口
1.为什么要把三个方法写道不同的接口当中?而不是直接写道Animal这个类中?
所有的Animal都会跑,游泳,飞吗?
假如我写一个Animal类里面有游泳,跑,飞的方法。
我用狗这个类继承Animal类就会使狗飞所以不能直接把所有方法写到动物中。
2.继承我们只能继承一个,但是接口的实现我可以有多个。
代码
(1)包
(2)包中的代码
Animal类
package demo3;
public abstract class Animal {
public String name ;
void run (){
}
public Animal(String name) {
this.name = name;
}
}
Bird类
package demo3;
public class Bird extends Animal implements IRon,IFly {
public Bird(String name) {
super(name);
}
@Override
public void run() {
System.out.println(this.name + "正在跳跃着跑");
}
@Override
public void fly() {
System.out.println(this.name+"正在开心的飞");
}
}
Dark类
package demo3;
public class Dark extends Animal implements IFly {
public Dark(String name) {
super(name);
}
public void fly(){
System.out.println(this.name+"烤鸭飞的很开心");
}
}
Dog类
package demo3;
public class Dog extends Animal implements IRon,ISwim{
public Dog(String name) {
super(name);
}
@Override
public void run() {
System.out.println("正在在跑");
}
@Override
public void swim() {
System.out.println(this.name+"正在游泳用四条腿");
}
}
IFly接口
package demo3;
public interface IFly {
void fly();
}
IRon接口
package demo3;
public interface IRon {
void run();
}
ISwam接口
package demo3;
public interface ISwim {
void swim();
}
在main中我分别对接口和抽象类用静态方法进行向上转型,分别是用Animal这个抽象类和接口这个抽象类,可以看到只要实现了接口的类都可以来对这个方法进行实现,这就体现出了接口的扩展性,当程序员使用接口以后会忘记类的类型。
4.2.1.3.4 接口的继承
1.在java中,类和类之间是单继承,一个类可以实现多个接口,接口与接口之间可以多继承,达到多继承的目的。
2.接口可以继承一个接口达到复用的效果,使用extends(扩展 )关键字
定义一个类和接口分别是青蛙和两栖动物
代码
Frog类代码
package demo3;
public class Frog extends Animal implements IAmphibious{
public Frog(String name) {
super(name);
}
@Override
public void test1() {
}
@Override
public void run() {
}
@Override
public void swim() {
}
}
IAmphibious接口代码
package demo3;
//接口和接口之间的关系用extends连接
//相当于加上自己的接口又具有其他接口。
public interface IAmphibious extends IRon,ISwim{
void test1();
}
这样青蛙就可以跑和游泳同时我们只需要实现一个接口就可以,因为两栖动物用extends关键字扩展了IRun和ISwam接口的特性,这就可以看出接口的运用是十分灵活的。
5.抽象类和接口的区别
1.抽象类和接口都是java中多态的常见使用方式,都需要重点掌握,同时又要认清两者的区别!!
2.核心区别,抽象类中可以包含普通的方法和普通的字段,这样普通的方法可以在子类中直接调用而不用重写,在接口中,不能包含普通方法,子类必须重写所有抽象方法,在接口中所有方法都是抽象方法。
3.抽象类存在的意义是为了更好的校验。防止我实例化一个抽象的类。例如Animal类在上面的代码有体现。
4.一个类只能继承一个抽象类但是可以实现多个接口,所以解决了java中不能多继承的特性。
(在java中没有全局变量)
6.object类
6.1 Object类与其他类的关系
1.这是一个nb的类(先说明一下)
2.它是所有类的父类,也就是说除了object类其他类不管是你新创建的类还是默认存在的类都是继承关系。(祖先类)
3.详细讲解Object类和其他类关系
代码讲解:我们定义了一个父类是Person而这个Person被Student这个类继承,又由于Person这个类也继承了一个类是Object类,所以Student类也继承了Person类。
package demo4;
class Person{
public String name ;
}
class Student extends Person{
}
public class test_1 {
}
6.2 Object类与其他类的向上转型
1.可以看到Object可以通过Person和Student类来进行向上转型,
2.也就是说当我们实在不知道该让这个子类被接收就可以用Object类进行接收。
代码
package demo4;
class Person{
public String name ;
}
class Student extends Person{
}
public class test_1 {
public static void main(String[] args) {
Person person = new Person();
Object obj = new Person() ;
Object obj_1 = new Student();
}
}
6.3 Object中的所有方法
1.展示所有方法,双击Shift点击all点击右上角的勾勾,输出Object点击java.lang就可以看到,我用的jdk8哈。
2.源码的话,大家自己进行调用然后理解这里不进行讲解。(不自己用是无法明白的)
6.3.2.Object类中方法使用。
1.toString()方法是用来获取对象的字符串表示形式,通过该方法返回的字符串通常包括该对象的重要属性信息。在默认情况下,Object类中的toString()方法返回的字符串形式为:类名@hashcode。
代码
package demo4;
class Person{
public String name = "Frank" ;
}
class Student extends Person{
public String toString(){
return name ;
}
}
public class test_1 {
public static void main(String[] args) {
// Person person = new Person();
// Object obj = new Person() ;
Object obj_1 = new Student();
System.out.println(obj_1+"是我的网名");
// String str_1 = "Frank";
// String str_2 ="Frank_2";
// System.out.println(str_1.equals(str_2));
}
}
输出结果
注意System.out.printin会自己自动调用toString方法
代码
class Person {
private String name;
private int age;
// 构造函数和其他方法省略
@Override
public String toString() {
return "Person{name=" + name + ", age=" + age + "}";
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person("Alice", 25);
System.out.println(person.toString()); // 手动调用toString()方法
System.out.println(person); // 由于println方法会自动调用对象的toString()方法,这里省略了toString()方法的显式调用
}
}
2.equals()方法:用来比较字符串的长度大小
(1)我们正常比较字符串的长度大小比较的是地址长度。
正常比较代码
package demo4;
class Person{
public String name ;
public Person(String name) {
this.name = name;
}
@Override
public String toString(){
return "name:"+ name ;
}
}
public class test_1 {
public static void main(String[] args) {
Person person = new Person("Frank");
Person person_2 = new Person("Frank_1");
System.out.println(person == person_2);
}
}
运行结果
(2)疑问,为什么输出结果同,明明判断的都是“zhangsan”这个字符串?
代码
package demo4;
class Person{
public String name ;
public Person(String name) {
this.name = name;
}
@Override
public String toString(){
return "name:"+ name ;
}
}
public class test_1 {
public static void main(String[] args) {
Person person = new Person("zhangsan");
Person person_2 = new Person("zhangsan");
System.out.println(person == person_2);
System.out.println(person.equals(person_2));//调用的是Object中
//的equales方法
String str_1 = "zhangsan";
String str_2 = "zhangsan";
System.out.println(str_1.equals(str_2));
}
}
运行结果
System.out.println(person.equals(person_2));//调用的是Object中
//的equales方法
这行代码中的equals我们点开它的源代码可以看到下图,我们发现object方法中的equals这里比较的还是地址。(默认比较地址),这样比较和没有调用equal方法的效果是一样的
所以为了满足我们的需求我们可以重写equals方法
(3)重写equals方法
public boolean equals(Object obj){
Person tmp = (Person) obj;//将Object类型的obj强制转换位Person类
return tmp.name.equals(this.name);//toString方法中的name和Person中的name两个name是不一样的
}
}
我们重写了这个方法以后,这时候toString中的equals会发现自己的equals也被重写了我们点开源码可以看到下图。
重写后的全部代码
package demo4;
class Person{
public String name ;
public Person(String name) {
this.name = name;
}
@Override
public String toString(){
return "name:"+ name ;
}
public boolean equals(Object obj){
Person tmp = (Person) obj;//将Object类型的obj强制转换位Person类
return tmp.name.equals(this.name);//toString方法中的name和Person中的name两个name是不一样的
}
}
public class test_1 {
public static void main(String[] args) {
Person person = new Person("zhangsan");
Person person_2 = new Person("zhangsan");
System.out.println(person == person_2);
System.out.println(person.equals(person_2));
String str_1 = "zhangsan";
String str_2 = "zhangsan";
System.out.println(str_1.equals(str_2));
}
}
运行结果
如果我们不想比较地址就要重写equals方法。
3.第三种方法Hashcode
(1)Hashcode方法在表述这个对象的位置,源码是由C++写的我们是看不到的。(源码被natice修饰的)
(2)如果我认为两个人是同一个人我是不是要放一个位置那么拿的哈希值是不是一样的。
但是结果却是不一样的,也就是Object提供的这个哈希方法是不能够满足我们的需求,所以我要对Hashcode方法进行重写。我们可以借助编译器来生成。(快捷键是(右键+Generate+equals and hashcode()+一直点next))这样就会根据name来生成hashcode和equals。
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
一般情况我们都可以借助编译器来生成,由于我是根据name生成的所以我的哈希值就是一样的
一般情况只要我自己定义的类,我们都会重写Hashcode和equals方法。