07-面向对象高级(面向对象)

感谢你的路过,希望学生的笔记能给你一点微不足道的参考(1/100)
Java面向对象思维导图,[完整Java体系的链接]
在这里插入图片描述

一,继承格式

1.1继承的由来?

   多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要 继承那一个类即可。多个类可以称为子类,单独那一个类称为父类、超类(superclass)或者基类。通过继承,可以使多种事物之间形成一种关系体系

1.2继承是怎么定义的?

    继承就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接 访问父类中的非私有的属性和行为。

1.3继承的好处有哪些?

   1. 提高代码的复用性
   2. 类与类之间产生了关系,是多态的前提

1.4继承的格式是怎样的?

   通过 extends 关键字,可以声明一个子类继承另外一个父类,定义格式如下:
      class 父类 {
       …
       }
      class 子类 extends 父类 {
      …
      }

1.5继承的限制?

   Java中只有单继承,多重继承,没有多继承。
在这里插入图片描述
   进行继承的内存分析时记住:
      创建子类对象前会先查看有无父类对象,有则先创建父类对象。在创建子类对象。
      子类对象中没有的属性或方法,会去父类中寻找。子类中的super()默认存储父类对象地址。
   例子:
在这里插入图片描述

1.6super关键字?

    通过super关键字,可以访问父类构造方法,属性,方法。调用super构造方法的代码,必须写在子类构造方法的第一行。
代码:

package JiCheng;

/**
 * @Description: TODO(父类) 
 * super:
 * 	1. 在子类的成员方法中,访问父类的成员变量。
	2. 在子类的成员方法中,访问父类的成员方法。
	3. 在子类的构造方法中,访问父类的构造方法。
		调用super构造方法的代码,必须写在子类构造方法的第一行。
 * @author: 王宇辉
 * @Date: 2021年8月17日 上午9:29:18
 */
public class Person {
	
	private String name;
	private int age;
	public String sex;
	
	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 Person() {
		
	}
	
	public Person(String name,int age) {
		this.name=name;
		this.age=age;
	}	
	
	public void say() {
        System.out.println("姓名:"+name+",年龄:"+age+",性别:"+sex);
    }
	
	public void method() {
        System.out.println("方法执行!");
    }
}
package JiCheng;

/**
 * @Description: TODO(子类) 
 * @author: 王宇辉
 * @Date: 2021年8月17日 上午9:30:28
 */
public class Assistant extends Person{

}
package JiCheng;
/**
 * @Description: TODO(子类及super关键字) 
 * @author: 王宇辉
 * @Date: 2021年8月17日 上午9:30:28
 */
public class Student extends Person{

	public Student() {
		super("无名",30);
		super.sex="男";
		super.method();
	}
}
package JiCheng;

public class Demo01_JiChengGeShi {
	/**
	 * 继承:是Java面向对象技术的基石,因为允许创建分等级层次的类。
	 * 继承就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。
	 * 子类可以直接 访问父类中的非私有的属性和行为。
	 * 格式:
	 * 	class 父类 { 
		... 
		}
		class 子类 extends 父类 { 
		...
		}
	 * @param args
	 */
	 public static void main(String[] args) {
	        // 创建了一个子类对象
	        Student s1 = new Student();
	        // s1.setName("王宇辉");
	        // s1.setAge(21);
	        // Teacher类会继承来自父类的method方法。
	        s1.say();

	        // 创建另一个子类的对象
	        Assistant assistant = new Assistant();
	        assistant.method();
	    }
}

二,重写与final?

2.1重写(Override) 规则:

    1、参数列表必须完全与被重写方法的相同,
    2、返回类型必须完全与被重写方法的返回类型相同;
    3、访问权限不能比父类中被重写的方法的访问权限更低。
        例如,如果父类的一个方法被声明为Pub1ic,那么在子类中重写该方法就不能声明为protected。
    4、父类的成员方法只能被它的子类重写。
    5、声明为static 和priVvete 的方法不能被重写,但是能够被再次声明。

2.2方法的覆盖重写特点:

    创建的是子类对象,则优先用子类方法.

2.3Java 中重写(Override) 与重载(Over1oad) 的区别

    1 、发生的位置
        重载,一个类中
        重写: 子父类中
    2、参数列表限制
        重载: 必须不同的
        重写: 必须相同的
    3、返回值类型
        重载: 与返回值类型无关
        重写: 返回值类型必须一致
    4、访问权限:
        重载: 与访问权限无关
        重写: 子的方法权限必须不能小于必父的方法权限
    5、异常处理;
        重载: 于异常无关
        重写,异常范围可以更小,但是不能抛出新的异常。
代码:

package ZhongXie;
/**
*重写(Override) 规则:
1、参数列表必须完全与被重写方法的相同, 
2、返回类型必须完全与被重写方法的返回类型相同;
3、访问权限不能比父类中被重写的方法的访问权限更低。
	例如,如果父类的一个方法被声明为Pub1ic,那么在子类中重写该方法就不能声明为protected。
4、父类的成员方法只能被它的子类重写。
5、声明为static 和priVvete 的方法不能被重写,但是能够被再次声明。

方法的覆盖重写特点:
	创建的是子类对象,则优先用子类方法.

Java 中重写(Override) 与重载(Over1oad) 的区别
1 、发生的位置
	重载,一个类中	
	重写: 子父类中
2、参数列表限制
	重载: 必须不同的
	重写: 必须相同的
3、返回值类型
	重载: 与返回值类型无关 
	重写: 返回值类型必须一致
4、访问权限:
	重载: 与访问权限无关
	重写: 子的方法权限必须不能小于必父的方法权限
5、异常处理;
	重载: 于异常无关 
	重写,异常范围可以更小,但是不能抛出新的异常。

 * @author: 王宇辉
 * @Date: 2021年8月17日 上午10:36:00
 */
public class Demo02_Override {
	
	public static void main(String[] args) {
      Student01 student = new Student01();
      student.say();
    }
}

class Person01{
	public void say() {
		System.out.println("111111111111111111");
	}
}

class Student01 extends Person01{
	public void say() {
		System.out.println("22222222222222222");
	}
}

2.4final关键字能干什么?

    用于修饰不可改变内容。
    final:
        类:被修饰的类,不能被继承
        方法:被修饰的方法,不能被重写
        变量:被修饰的变量,不能被重新赋值

2.5final关键字的使用方式?

    如何修饰类?
        格式如下:
            final class 类名 {
            }
    如何修饰方法?
        格式如下:
            修饰符 final 返回值类型 方法名(参数列表){
            //方法体
            }
        重写被 final 修饰的方法,编译时就会报错。
        注意事项:
            对于类、方法来说,abstract关键字和final关键字不能同时使用,因为矛盾。
    如何修饰变量?
        局部变量——基本类型
           基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改。
        局部变量——引用类型
           引用类型的局部变量,被final修饰后,只能指向一个对象,地址不能再更改。但是不影响对象内部的成员变量值的修改。
        成员变量
           成员变量涉及到初始化的问题,初始化方式有两种,只能二选一:
           显示初始化
               被final修饰的常量名称,一般都有书写规范,所有字母都大写。
        构造方法初始化
       其他:
           1. 由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了。
           2. 对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值。二者选其一。
           3. 必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值。
代码:

package Final;
/*
final关键字代表最终、不可改变的。

常见四种用法:
1. 可以用来修饰一个类
2. 可以用来修饰一个方法
3. 还可以用来修饰一个局部变量
4. 还可以用来修饰一个成员变量
 */
public class Demo03_Final {
	/**
	 * TODO final代码先放这里(不甚了解,目前影响不大。等之后在看看)
	 * final关键字
		final 用于修饰属性、变量-
		变量成为了常量,无法对其再次进行赋值。
		final 修饰的局部变量,只能赋值一次(可以先声明后赋值) 和final 修饰的是成员属性,必须在声明时赋值。 
		全局常量( public static final )

		常量的命名规范:
		由工个或多个单词组成,单词与单词之间必须使用下划线隔开,单词中所有字母大写    例如: SQL_INSERT

		final 用于修饰类
		final]修饰的类,不可以被继承。 final 用于修饰方法
		final 修饰的方法,不能被子类重写。

	 * 	Demo03_Final-(final关键字)
		MySubClass.java-(子类)
		Person.java-(修饰成员变量)
		Student.java-(修饰局部变量)
		Zi.java-(方法子类)
		Demo01Final.java-(对象)
		Fu.java-(修饰方法)
		MyClass.java-(修饰类)
	 * @param args
	 */
	 public static void main(String[] args) {
	        int num1 = 10;
	        System.out.println(num1); // 10
	        num1 = 20;
	        System.out.println(num1); // 20

	        // 一旦使用final用来修饰局部变量,那么这个变量就不能进行更改。
	        // “一次赋值,终生不变”
	        final int num2 = 200;
	        System.out.println(num2); // 200

//	        num2 = 250; // 错误写法!不能改变!
//	        num2 = 200; // 错误写法!

	        // 正确写法!只要保证有唯一一次赋值即可
	        final int num3;
	        num3 = 30;

	        // 对于基本类型来说,不可变说的是变量当中的数据不可改变
	        // 对于引用类型来说,不可变说的是变量当中的地址值不可改变
	        Student stu1 = new Student("赵丽颖");
	        System.out.println(stu1);
	        System.out.println(stu1.getName()); // 赵丽颖
	        stu1 = new Student("霍建华");
	        System.out.println(stu1);
	        System.out.println(stu1.getName()); // 霍建华
	        System.out.println("===============");

	        final Student stu2 = new Student("高圆圆");
	        // 错误写法!final的引用类型变量,其中的地址不可改变
//	        stu2 = new Student("赵又廷");
	        System.out.println(stu2.getName()); // 高圆圆
	        stu2.setName("圆圆圆圆");
	        System.out.println(stu2.getName()); // 高圆圆圆圆圆圆
	    }
}
package Final;
/*
当final关键字用来修饰一个方法的时候,这个方法就是最终方法,也就是不能被覆盖重写。
格式:
修饰符 final 返回值类型 方法名称(参数列表) {
    // 方法体
}

注意事项:
对于类、方法来说,abstract关键字和final关键字不能同时使用,因为矛盾。
 */
public abstract class Fu {
    public final void method() {
        System.out.println("父类方法执行!");
    }
    public abstract /*final*/ void methodAbs() ;
}
package Final;

public class Zi extends Fu{
    @Override
    public void methodAbs() {

    }

 // 错误写法!不能覆盖重写父类当中final的方法
//  @Override
//  public void method() {
//      System.out.println("子类覆盖重写父类的方法!");
//
}
package Final;
/*
当final关键字用来修饰一个类的时候,格式:
public final class 类名称 {
    // ...
}

含义:当前这个类不能有任何的子类。(太监类)
注意:一个类如果是final的,那么其中所有的成员方法都无法进行覆盖重写(因为没儿子。)
 */
public final class MyClass /*extends Object*/  {
    public void method() {
        System.out.println("方法执行!");
    }
}
package Final;
//不能使用一个final类来作为父类
public class MySubClass /*extends MyClass*/{
}
package Final;
/*
对于成员变量来说,如果使用final关键字修饰,那么这个变量也照样是不可变。

1. 由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了。
2. 对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值。二者选其一。
3. 必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值。
 */
public class Person {
    private final String name/* = "鹿晗"*/;

    public Person() {
        name = "关晓彤";
    }

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

//    public void setName(String name) {
//        this.name = name;
//    }
}
package Final;

public class Student {
    private String name;

    public Student() {
    }

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

三,抽象类

3.1抽象类是怎么来的?

    父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有意义,而方法主体则没有存在的意义了。我们把没有方法主体的方法称为抽象方法。Java语法规定,包含抽象方法的类就是抽象类。
    抽象方法 : 没有方法体的方法
    抽象类:包含抽象方法的类

3.2抽象类概念

    抽象类必须使用abstract class声明 一个抽象类中可以没有抽象方法。抽象方法必须写在抽象类或者接口中。
        格式:
        abstract class 类名{ // 抽象类
        }

3.3抽象方法

    只声明而未实现的方法称为抽象方法(未实现指的是:没有“{}”方法体),抽象方法必须使用abstract关键字声明。
    格式:
        abstract class 类名{ // 抽象类
        public abstract void 方法名() ; // 抽象方法,只声明而未实现
        }

3.4抽象类怎么使用?

    继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该父 类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法。

3.5不能被实例化

    在抽象类的使用中有几个原则:
        ·抽象类本身是不能直接进行实例化操作的,即:不能直接使用关键字new完成。
·         一个抽象类必须被子类所继承,被继承的子类(如果不是抽象类)则必须覆写(重写)抽象类中的全部抽象方法。

3.6常见问题

    1、 抽象类能否使用final声明?
        不能,因为final属修饰的类是不能有子类的 , 而抽象类必须有子类才有意义,所以不能。
    2、 抽象类能否有构造方法?
        能有构造方法,而且子类对象实例化的时候的流程与普通类的继承是一样的,都是要先调用父类中的构造方法(默 认是无参的),之后再调用子类自己的构造方法。

3.7与普通类的区别

    1、抽象类必须用public或protected修饰(如果为private修饰,那么子类则无法继承,也就无法实现其抽象方法)。
默认缺省为 public
    2、抽象类不可以使用new关键字创建对象, 但是在子类创建对象时, 抽象父类也会被JVM实例化。
    3、如果一个子类继承抽象类,那么必须实现其所有的抽象方法。如果有未实现的抽象方法,那么子类也必须定义为 abstract类

3.8@Override标签

    位置:子类重写抽象类中的抽象方法时,在子类重写方法上的注解。
    作用:
        1. 可以给你当作注释用。
        2. 可以告诉读你代码的人,这是对它父类方法的重写。
        3. 编译器可以给你验证@Override下面的方法名称是否是你父类中所有的,如果没有就会报错。
    例子:
        当你想要在子类中重写父类的一个方法,但是你把名字打错了,当你写了@Override编译器会提示你,你写的这个方法父类中没有;但是如果你没有写@Override编译器就会觉得这个是你子类中写的新的方法,并不会报错。
代码:

package ChouXiangLei;

public class Demo04_ChouXiang {
	/**
	 * 抽象类的使用
	 * @param args
	 */
	public static void main(String[] args) {
		Student02 student02 = new Student02();
		student02.say();
		
		// Person02 person02 = new Person02();
		//报错了.因为抽象类本身是不能直接进行实例化操作的,即:不能直接使用关键字new完成。 
}
}
package ChouXiangLei;

/**
 * @Description: TODO(定义一个抽象类与抽象方法) 
 * @author: 王宇辉
 * @Date: 2021年8月17日 上午11:43:14
 */
public abstract class Person02 {
	
	public Person02() {
		// 抽象类中能有构造方法,而且子类对象实例化的时候的流程与普通类的继承是一样的,
		//都是要先调用父类中的构造方法(默 认是无参的),之后再调用子类自己的构造方法。 
		System.out.println("构造方法执行了");
	}
	
	public abstract void say() ;
}
package ChouXiangLei;

public class Student02 extends Person02{

	@Override
	public void say() {
		// TODO Auto-generated method stub
		System.out.println("子类继承抽象父类");
	}
}

四,接口

4.1什么是接口?

    接口是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么 接口的内部主要就是封装了方法,包含抽象方法和常量,默认方法和静态方法,私有方法 ,是一种公共的规范标准。

4.2定义格式:

    nterface 接口名称{
    全局常量 ;
    抽象方法 ;
    }

4.3面向接口编程思想

    接口是定义(规范,约束)与实现(名实分离的原则)的分离。
    优点:
        1、 降低程序的耦合性
        2、 易于程序的扩展
        3、 有利于程序的维护

4.4接口怎么被使用的?

    接口的使用,它不能创建对象,但是可以被实现( implements ,类似于被继承)。一个实现接口的类(可以看做 是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象 类。

4.5接口的使用步骤?

    1. 接口不能直接使用,必须有一个“实现类”来“实现”该接口。
        格式:
        public class 实现类名称 implements 接口名称 {
        // …
        }
    2. 接口的实现类必须覆盖重写(实现)接口中所有的抽象方法。
        实现:去掉abstract关键字,加上方法体大括号。
    3. 创建实现类的对象,进行使用。

4.6全局常量和抽象方法的简写

    因为接口本身都是由全局常量和抽象方法组成 , 所以接口中的成员定义可以简写:
    1、全局常量编写时, 可以省略public static final 关键字,例如:
        public static final String INFO = “内容” ;
        简写后:String INFO = “内容” ;
    2、抽象方法编写时, 可以省略 public abstract 关键字, 例如:
        public abstract void print() ;
        简写后: void print() ;

4.7接口的实现 implements

    接口可以多实现:
    格式:
        class 子类 implements 父接口1,父接口2…{
        }
    以上的代码称为接口的实现。那么如果一个类即要实现接口,又要继承抽象类的话,则按照以下的格式编写即可:
        class 子类 extends 父类 implements 父接口1,父接口2…{
        }

4.8接口和抽象类的区别

    1、抽象类要被子类继承,接口要被类实现。
    2、接口只能声明抽象方法,抽象类中可以声明抽象方法,也可以写非抽象方法。
    3、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
    4、抽象类使用继承来使用, 无法多继承。 接口使用实现来使用, 可以多实现
    5、抽象类中可以包含static方法 ,但是接口中不允许(静态方法不能被子类重写,因此接口中不能声明静态方法)
    6、接口不能有构造方法,但是抽象类可以有

4.9注意

    如果一个接口要想使用,必须依靠子类。 子类(如果不是抽象类的话)要实现接口中的所有抽象方法。
    补充:接口继承
    接口因为都是抽象部分, 不存在具体的实现, 所以允许多继承,例如:
        interface C extends A,B{
        }
代码:

package JieKou;

public class Demo05_JieKou {

	/**
	 * 接口
	 * @param args
	 */
	public static void main(String[] args) {
		Student03 s1 = new Student03();
		s1.say();
	}
}
package JieKou;

public interface Persion03 {
	 void say();
}
package JieKou;

public class Student03 implements Persion03{

	@Override
	public void say() {
		System.out.println("接口方法调用成功");
		
	}

}

五,多态

5.1多态的概念?

    多态:就是对象的多种表现形式,(多种体现形态)

5.2多态的前提是什么?

    1. 继承或者实现【二选一】
    2. 方法的重写【意义体现:不重写,无意义】
    3. 父类引用指向子类对象【格式体现】

5.3多态的体现?

    对象的多态性,从概念上非常好理解,在类中有子类和父类之分,子类就是父类的一种形态 ,对象多态性就从此而来。
    ps: 方法的重载 和 重写 也是多态的一种, 不过是方法的多态(相同方法名的多种形态)。
        重载: 一个类中方法的多态性体现
        重写: 子父类中方法的多态性体现。

5.4多态的使用?

    类似于基本数据类型的转换:
        · 向上转型:将子类实例变为父类实例
            |- 格式:父类 父类对象 = 子类实例 ;
        · 向下转型:将父类实例变为子类实例
            |- 格式:子类 子类对象 = (子类)父类实例 ;

5.5多态的好处有哪些?

    实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展性与便利。多态的好处,体现在,可以使程序编写的更简单,并有良好的扩展。

5.6instanceof

    作用:
        判断某个对象是否是指定类的实例,则可以使用instanceof关键字
    格式:
        实例化对象 instanceof 类 //此操作返回boolean类型的数据

代码:

package DuoTai;

/**
 * @Description: TODO(父类) 
 * super:
 * 	1. 在子类的成员方法中,访问父类的成员变量。
	2. 在子类的成员方法中,访问父类的成员方法。
	3. 在子类的构造方法中,访问父类的构造方法。
		调用super构造方法的代码,必须写在子类构造方法的第一行。
 * @author: 王宇辉
 * @Date: 2021年8月17日 上午9:29:18
 */
public class Person03 {
	
	public void say() {
        System.out.println("父类方法");
    }
	
	public void method() {
        System.out.println("方法执行!");
    }
}
package DuoTai;

/**
 * @Description: TODO(子类) 
 * @author: 王宇辉
 * @Date: 2021年8月17日 上午9:30:28
 */
public class Assistant03 extends Person03{
	public void say() {
        System.out.println("我是护士");
    }
}
package DuoTai;
/**
 * @Description: TODO(子类及super关键字) 
 * @author: 王宇辉
 * @Date: 2021年8月17日 上午9:30:28
 */
public class Student03 extends Person03{

	public void say() {
        System.out.println("我是学生");
    }
}
package DuoTai;

public class Demo06_DuoTai {
	/**
	 * 多态
	 * @param args
	 */
	public static void main(String[] args) {
		
		/*
		 * 多态的使用
		 * Person03 p1 =null; Student03 s1 = new Student03();
		 * p1=s1; p1.say();
		 */
		
		// 使用多态时的注意细节
		Student03 s2 = new Student03();
		Assistant03 a2 = new Assistant03();
		
		Person03 p1 =s2;
		Person03 p2 =a2;
		
		p1.say();
		p2.say();
		
		Student03 s3 = (Student03)p1;
		// Student03 s4 = (Student03)p2;// 有问题:因为护士不是学生
		
		s3.say();
		// s4.say();
		
		// instanceof关键字
		Assistant03 a4 = new Assistant03();
		say(a4);
		Student03 s4 = new Student03();
		say(s4);	
	}

	public static void say(Person03 p) {
	// 如何判断传入的对象时此类型的那种形态(哪个子类的对象)
	if (p instanceof Student03) {
		Student03 s = (Student03)p;
		s.say();
	} else {
		System.out.println("必须传入学生形态");
	}
}
}

六,Object类

6.1什么是Object类?

    Object类是所有类的父类(基类),如果一个类没有明确的继承某一个具体的类,则将默认继承Object类。
    例如我们定义一个类:
        public class Person{
        }
    其实它被使用时 是这样的:
        public class Person extends Object{
&n    bsp;   }

6.2Object的多态性:

    使用Object可以接收任意的引用数据类型

6.3我们要学Object类中的方法?

    toString方法
        public String toString()`:返回该对象的字符串表示。其实该字符串内容就是对象的类型+@+内存地址值。

        toString方法的覆盖重写?
         为什么要有覆盖重写?
            由于toString方法返回的结果是内存地址,而在开发中,经常需要按照对象的属性得到相应的字符串表现形式,因此也需要重写它。
如果不希望使用toString方法的默认行为,则可以对它进行覆盖重写。例如自定义的Person类。
   equals方法
      public boolean equals(Object obj)`:指示其他某个对象是否与此对象“相等”。调用成员方法equals并指定参数为另一个对象,则可以判断这两个对象是否是相同的。这里的“相同”有默认和自定义两种方式。

      equals方法的默认地址比较?
         如果没有覆盖重写equals方法,那么Object类中默认进行==运算符的对象地址比较,只要不是同一个对象,结果必然为false。
      equals方法的对象内容比较?
         如果希望进行对象的内容比较,即所有或指定的部分成员变量相同就判定两个对象相同,则可以覆盖重写equals方法。
      equals方法里的覆盖重写
         为什么要覆盖重写?
            因为equals方法比较的是两个对象的地址值,没有意义。我们需要比较两个对象的属性值。
      equals方法重写时的五个特性:
         自反性 :对于任何非空的参考值x , x.equals(x)应该返回true 。
         对称性 :对于任何非空引用值x和y , x.equals(y)应该返回true当且仅当y.equals(x)回报true 。
         传递性 :对于任何非空引用值x , y和z ,如果x.equals(y)回报true个y.equals(z)回报true ,然后 x.equals(z)应该返回true 。
         一致性 :对于任何非空引用值x和y ,多次调用x.equals(y)始终返回true或始终返回false ,前提是未修改对象 上的equals比较中使用的信息。
         非空性 :对于任何非空的参考值x , x.equals(null)应该返回false 。

6.4equals与==方法小总结?

   == 的作用:
      基本类型:比较的就是值是否相同
      引用类型:比较的就是地址值是否相同
   equals 的作用:
      引用类型:默认情况下,比较的是地址值。
      不过,我们可以根据情况自己重写该方法。一般重写都是自动生成,比较对象的成员变量值是否相同
代码:

package Object;

public class Person05 {
	
	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;
	}
	
	
	public Person05() {
		super();
	}
	public Person05(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	
	
	@Override
	public String toString() { // 重写toString方法
		return "Person05 [name=" + name + ", age=" + age + "]";
	}	
	
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (o == null) {
			return false;
		}
		if (o instanceof Person05) {
			Person05 person05 = (Person05)o;
			if (this.name.equals(person05.name) && this.age == person05.age) {
				// 相同
				return true;
			} 
		} 
			return false;
	}
	
}
package Object;

public class Demo07_Object {
	/**
	 * Object类
	 * @param args
	 */
	public static void main(String[] args) {
		// toString方法
		Person05 p = new Person05("张三",18);
		System.out.println(p);
		Person05 p1 = new Person05("张三",18);
		System.out.println(p1);
		
		System.out.println(p==p1);
		System.out.println(p.equals(p1));
		
	}
}

七,内部类

7.1概念与分类?

   在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。
   广泛意义上的内部类一般来说包括这四种:
      1、成员内部类
      2、局部内部类
      3、匿名内部类
      4、静态内部类

7.2内部类的访问特点?

   内部类可以直接访问外部类的成员,包括私有成员。
   外部类要访问内部类的成员,必须要建立内部类的对象。
   如何创建内部类对象格式:
      外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
      内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的.class文件,但是前面冠以外部类的类名 和符号 。 比如,Person$Heart.class

7.3成员内部类?

定义:位位于另一个类的内部,形式: 
					class Outer { 
					private double x = 0; 
					public Outer(double x) { 
					this.x = x; 
					}
					class Inner { //内部类 
					public void say() { 
					System.out.println("x="+x); 
					} 
					} 
					}
特点: 成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。 不过要注意的是,当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问 的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问: 
	外部类.this.成员变量 
	外部类.this.成员方法 
外部使用成员内部类 
	Outter outter = new Outter(); 
	Outter.Inner inner = outter.new Inner(); 

代码:

package NeiBuLei.ChengYuan;

// 如果出现了重名现象,那么格式是:外部类名称.this.外部类成员变量名
public class Outer {
	private int num; // 外部类的成员变量

    public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}

	public class Inner /*extends Object*/ {

		private int num =  20; // 内部类的成员变量
        public void methodInner() {
            int num = 30; // 内部类方法的局部变量
            System.out.println(num); // 局部变量,就近原则
            System.out.println(this.num); // 内部类的成员变量
            System.out.println(Outer.this.num); // 外部类的成员变量
        }

    }
}
package NeiBuLei.ChengYuan;

public class Demo08_ChengYuan {
	/**
	 * 成员内部类
	 * @param args
	 */
	public static void main(String[] args) {
		Outer o = new Outer();
		o.setNum(101);
		
		Outer.Inner inner  = o.new Inner();
		inner.methodInner();
		
		// 第二种使用格式
		Outer.Inner s = new Outer().new Inner();
		s.methodInner();
		
	}
}

7.4局部内部类?

局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或 者该作用域内。 
定义格式:
	修饰符 class 外部类名称 {
	    修饰符 返回值类型 外部类方法名称(参数列表) {
	        class 局部内部类名称 {
	            // ...
	        }
	    }
	}
例子:
	class Person{ 
	public Person() { 
	} }
	class Man{ 
	public Man(){ 
	}
	public People getPerson(){ 
	class Student extends People{ //局部内部类 
	int age =0; 
	}
	return new Student(); 
	} 
	} 
如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。“局部”:只有当前所属的方法才能使用它,出了这个方法外面就不能用了。
注意:局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。

代码:

package NeiBuLei.JuBu;
/*
如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。
“局部”:只有当前所属的方法才能使用它,出了这个方法外面就不能用了。

定义格式:
修饰符 class 外部类名称 {
    修饰符 返回值类型 外部类方法名称(参数列表) {
        class 局部内部类名称 {
            // ...
        }
    }
}

小节一下类的权限修饰符:
public > protected > (default) > private
定义一个类的时候,权限修饰符规则:
1. 外部类:public / (default)
2. 成员内部类:public / protected / (default) / private
3. 局部内部类:什么都不能写
 */
public class Outer01 {
    public void methodOuter() {
        class Inner { // 局部内部类
            int num = 10;
            public void methodInner() {
                System.out.println(num); // 10
            }
        }

        Inner inner = new Inner();
        inner.methodInner();
    }
}
package NeiBuLei.JuBu;

public class Demo09_JuBu {
	/**
	 * 局部内部类
	 * @param args
	 */
	public static void main(String[] args) {
		 Outer01 obj = new Outer01();
	     obj.methodOuter();
	}
}

7.5匿名内部类

什么是匿名内部类?
	是内部类的简化写法。它的本质是一个带具体实现的 父类或者父接口的匿名的子类对象。
格式:
	new 父类名或者接口名(){ 
	// 方法重写 
	@Override 
	public void method() { 
	// 执行语句 
	} 
	};
在这里我们看到使用匿名内部类我们必须要继承一个父类或者实现一个接口,当然也仅能只继承一个父类或者实现一 个接口。同时它也是没有class关键字,这是因为匿名内部类是直接使用new来生成一个对象的引用。当然这个引用是隐式的。 
注意:
	1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。 
	2、匿名内部类中是不能定义构造函数的。 
	3、匿名内部类中不能存在任何的静态成员变量和静态方法。 
	4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。 
	5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。 
	6、只能访问final型的局部变量
为什么局部和匿名内部类只能访问final型的局部变量?
因为内部类会被单独编译成一个字节码文件,为了保证编译后的文件中的备份的变量与外部的变量值完全一样,系统从规则上限制了变量的值不可更改。

代码:

package NeiBuLei.NiMing;

public interface MyInterface {
    void  method1();
    void  method2();
}
package NeiBuLei.NiMing;

public class MyInterfaceImpl implements MyInterface {
    @Override
    public void method1() {
        System.out.println("实现类覆盖重写了方法!111");
    }

    @Override
    public void method2() {
        System.out.println("实现类覆盖重写了方法!222");
    }
}
package NeiBuLei.NiMing;
/*
如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,
那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】。

匿名内部类的定义格式:
接口名称 对象名 = new 接口名称() {
    // 覆盖重写所有抽象方法
};

对格式“new 接口名称() {...}”进行解析:
1. new代表创建对象的动作
2. 接口名称就是匿名内部类需要实现哪个接口
3. {...}这才是匿名内部类的内容

另外还要注意几点问题:
1. 匿名内部类,在【创建对象】的时候,只能使用唯一一次。
如果希望多次创建对象,而且类的内容一样的话,那么就需要使用单独定义的实现类了。
2. 匿名对象,在【调用方法】的时候,只能调用唯一一次。
如果希望同一个对象,调用多次方法,那么必须给对象起个名字。
3. 匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】
强调:匿名内部类和匿名对象不是一回事!!!
 */
public class Demo10_NiMing {
	/**
	 * 匿名内部类的使用及其注意
	 * @param args
	 */
	 public static void main(String[] args) {
//       MyInterface obj = new MyInterfaceImpl();
//       obj.method();

//       MyInterface some = new MyInterface(); // 错误写法!

       // 使用匿名内部类,但不是匿名对象,对象名称就叫objA
       MyInterface objA = new MyInterface() {
           @Override
           public void method1() {
               System.out.println("匿名内部类实现了方法!111-A");
           }

           @Override
           public void method2() {
               System.out.println("匿名内部类实现了方法!222-A");
           }
       };
       objA.method1();
       objA.method2();
       System.out.println("=================");

       // 使用了匿名内部类,而且省略了对象名称,也是匿名对象
       new MyInterface() {
           @Override
           public void method1() {
               System.out.println("匿名内部类实现了方法!111-B");
           }

           @Override
           public void method2() {
               System.out.println("匿名内部类实现了方法!222-B");
           }
       }.method1();
       // 因为匿名对象无法调用第二次方法,所以需要再创建一个匿名内部类的匿名对象
       new MyInterface() {
           @Override
           public void method1() {
               System.out.println("匿名内部类实现了方法!111-B");
           }

           @Override
           public void method2() {
               System.out.println("匿名内部类实现了方法!222-B");
           }
       }.method2();
   }
}

7.6静态内部类

静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。 
静态内部类是不需要依赖于外部类对象的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法。
格式: 
	public class Test { 
	public static void main(String[] args) { 
	Outter.Inner inner = new Outter.Inner(); 
	} 
	}
	class Outter { 
	public Outter() { 
	}
	static class Inner { 
	public Inner() { 
	} 
	} 
	}

八,包装类

8.1为什么要有封装类?

   因为在Java中有“一切皆对象”的原则,但这样一来Java中的基本的数据类型,就完全不符合于这种设计思想,因为Java中的八种基本数据类型并不是引用数据类型,所以Java中为了解决这样的问题,引入了八种基本数据类型的包装类。

8.2怎么理解包装类?

   可以把创建基本类型理解为直接在栈里创建(int = x)的,而包装类型是在堆里创建(int = x)的。栈里接收的只是一个地址Integer=(int = x)的地址。

8.3八种封装类?

   基本类型 对应的包装类
    byte Byte
   short Short
   int Integer
   long Long
   float Float
   double Double
   char Character
   boolean Boolean
以上的八种包装类也可分为两大的类型:
· Number:Integer、Short、Long、Double、Float、Byte都是Number的子类表示是一个数字。
· Object:Character、Boolean都是Object的直接子类。

8.4装箱与拆箱?

   将一个基本数据类型变为包装类,那么这样的操作称为装箱操作。
   将一个包装类变为一个基本数据类型,这样的操作称为拆箱操作,
   因为所有的数值型的包装类都是Number的子类,Number的类中定义了如下的操作方法,以下的全部方法都是进行拆箱的操作。

8.5自动装箱拆箱?

   由于我们经常要做基本类型与包装类之间的转换,从Java 5(JDK 1.5)开始,基本类型与包装类的装箱、拆箱动作可以自动完成。

8.6字符串转换 ?

   基本类型转换为String
      基本类型转换String总共有三种方式,查看课后资料可以得知,这里只讲最简单的一种方式:
      基本类型直接与””相连接即可;如:34+""
   String转换成对应的基本类型(除了Character类之外,其他所有包装类都具有parseXxx静态方法可以将字符串参数转换为对应的基本类型:)
    - public static byte parseByte(String s):将字符串参数转换为对应的byte基本类型。
    - public static short parseShort(String s):将字符串参数转换为对应的short基本类型。
    - public static int parseInt(String s):将字符串参数转换为对应的int基本类型。
    - public static long parseLong(String s):将字符串参数转换为对应的long基本类型。
    - public static float parseFloat(String s):将字符串参数转换为对应的float基本类型。
    - public static double parseDouble(String s):将字符串参数转换为对应的double基本类型。
    - public static boolean parseBoolean(String s):将字符串参数转换为对应的boolean基本类型。
代码:

package BaoZhuangLei;

import java.util.ArrayList;

public class Demo11_BaoZhuangLei {

	public static void main(String[] args) {
		//装箱:把基本类型的数据,包装到包装类中(基本类型的数据->包装类)
        //构造方法
        Integer in1 = new Integer(200);//方法上有横线,说明方法过时了
        System.out.println(in1);//200 重写了toString方法
           
        //拆箱:在包装类中取出基本类型的数据(包装类->基本类型的数据)
        int i = in1.intValue();
        System.out.println(i);
        
        System.out.println("--------------------------");
        
       // 自动装箱:直接把int类型的整数赋值包装类
        Integer in = 1;
        
       /* 自动拆箱:in是包装类,无法直接参与运算,可以自动转换为基本数据类型,在进行计算
        in+2;就相当于 in.intVale() + 2 = 3
        in = in.intVale() + 2 = 3 又是一个自动拆箱*/
        in = in+2;
        
        // ArrayList集合无法直接存储整数,可以存储Integer包装类
        ArrayList<Integer> list = new ArrayList<>();
  
        list.add(1); //-->自动装箱 list.add(new Integer(1));
        int i0 = list.get(0); //-->自动拆箱  list.get(0).intValue();
        System.out.println(i0);
        
        System.out.println("--------------------------");
        
        //基本类型->字符串(String)
        int i1 = 100;
        String s1 = i1+"";
        System.out.println(s1+200);//100200

        String s2 = Integer.toString(100);
        System.out.println(s2+200);//100200

        String s3 = String.valueOf(100);
        System.out.println(s3+200);//100200

        //字符串(String)->基本类型
        int i11 = Integer.parseInt(s1);
        System.out.println(i11-10);

        int a1 = Integer.parseInt("a1");//NumberFormatException
        // 如果字符串参数的内容无法正确转换为对应的基本类型,则会出`java.lang.NumberFormatException`异常。
        System.out.println(a1);
	}
	
}

九,可变参数

   一个方法中定义完了参数,则在调用的时候必须传入与其一一对应的参数,但是在JDK 1.5之后提供了新的功能,可以根据需要自动传入任意个数的参数。
   语法:
      返回值类型 方法名称(数据类型…参数名称){
      //参数在方法内部 , 以数组的形式来接收
      }
   注意:可变参数只能出现在参数列表的最后。
代码:

public class Demo12_KeBianCanShu {
	
	
	/**
	 * 可变参数
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println(sum(1));
		System.out.println(sum(1,2,3));
		System.out.println(sum(1,2,3,4));
		System.out.println(sum(1,2,3,4,5));
	}
	
	/**
	 * int... nums:表示的是可变参数,调用时可以传递0—N个数字在方法内部。
	 * 在方法内部,可变参数以数组作为载体体现
	 * @param nums
	 * @return
	 */
	public static int sum(int... nums) {
		int n = 0;
		for (int i = 0; i < nums.length; i++) {
			n+=nums[i];
		}
		return n;
	}
}

十,递归

   递归,在数学与计算机科学中,是指在方法的定义中使用方法自身。也就是说,递归算法是一种直接或者间接调用自身方
法的算法。
递归流程图如下:(感觉完全不用解释啊)
在这里插入图片描述
代码:

/**
 * 递归的三大要素
 * 第一要素:明确你这个函数想要干什么
 * 	功能:求n 的阶乘
 * 第二要素:寻找递归结束条件
 * 	我们需要找出当参数为啥时,递归结束,之后直接把结果返回,请注意,
 * 	这个时候我们必须能根据这个参数的值,能够直接知道函数的结果是什么。
 * 	结束条件: i为1/2
 * 第三要素:找出函数的等价关系式,不断缩小参数的范围
 * 	f(n) = n * f(n-1)
 * @author: 王宇辉
 * @Date: 2021年8月23日 下午4:07:00
 */
public class Demo13_DiGui {
	/**
	 * 递归实现5的阶乘
	 * @param args
	 */
	public static void main(String[] args) {
		// System.out.println(5*4*3*2*1);
		int n = fact(5);
		System.out.println(n);
	}

	private static int fact(int i) {
		if (i <= 2) {
			return 1;
		} else {
			return i*fact(i-1);
		}
		
	}
}

十一,综合练习

任务一:
1、试题:假设用户账号为:admin,密码为 123,编写用户登陆案例。 要
求:请将登陆定义为 login 方法, 并将 login 方法写在 UserService 类中
2、试题:自定义一个类, 命名为 MyList,类中包含属性:Object[] element;
定义如下几个方法:

  1. 增加方法 add : 可以向数组属性中依次存储 Object,数组内容存满时,需实现动态扩容(详解在下面)。 参考: Boolean add(Object
    obj)
  2. 删除方法 remove : 可以根据数据或下标,从数组属性中删除 Object 数据,删除后,数组后续 元素需前移。参考:void remove(Object
    obj) 或 void remove(Integer index)
  3. 查询方法 get : 方法传入下标,返回数组中指定下标的数据。
    参考:Object get(Integer index)
  4. 当前存储数据量 size : 获取当前存储的有效数据长度。参考:size=数组.length

任务二:

  1. 定义机器类,以及拳头属性(此属性只有三个值:剪刀,石头,布。这里
    的值可以使用数值代替)
  2. 定义生成随机数的方法(让机器生成剪刀,石头,布的值),赋值给第一
    步的拳头属性
  3. 定义测试类,获取用户输入的剪头石头布的值,和随机生成的值比较
  4. 测试中,定义变量保存胜者积分

具体代码见资料

参考:距离记笔记的时候过了一段时间,如果我的笔记有明显借鉴了您的内容,请见谅。(我记忆力很差的,当初写笔记的时候,参考文档记录的可能不全)
资料下载链接(笔记+代码+其他):百度网盘
链接:https://pan.baidu.com/s/1jwbFckzdpmdRolmnvSwfmg
提取码:1111
感谢阅读,祝你从此一帆风顺。


版权声明:本文为weixin_42688085原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。