JavaSE【第五章】面向对象

JavaSE【第五章】面向对象

引言!

面向对象是Java核心的内容部分,在学习的过程中首先需要您掌握各种知识点,利用知识点进行程序的设计。面向对象的运用是一个漫长的过程,关系到程序设计的各个方面,不仅仅是编写代码部分。

面向对象的相关内容是比较抽象的,掌握的难度上会有所增加。

举例:

Scanner   sc = new  Scanner(System.in);

分析:

1、Scanner 就是Java中的一个class类。

2new 关键字的含义。 ----new其实就是在堆内存中开辟空间创建对象。

3、Scanner(System.in); ---- 是啥?

   答:调用Scanner类的构造,创建出sc这个对象。System.in仅仅是提供的一个参数而已!
   
4、通过调用sc对象中的各种方法来扫描控制台不同类型的数据。   

得出结论,面向对象会学习哪些内容:

1、面向对象会教我们如何合理的设计出一个我们自己的类。

2、如何通过类去创建出对象-构造方法。

3、类在Java程序中是可以存在层级关系的(继承)。

4、由于继承的存在,会出现方法的重写,从而形成(多态)。

5、类不能完全的去表达我们现实生活中的一切,会涉及到(抽象类)。

6、根据开发的需求,我们往往需要进行功能的约定,需要学习到(接口)。

1、如何定义一个类?

​ a、属性

​ b、方法

2、构造方法?

​ a、如何创建对象

​ b、如何使用对象

3、类的关系(继承)

​ a、如何实现继承

​ b、继承的优劣势

​ c、继承对构造的依赖

4、多态性

​ a、什么叫多态

​ b、方法的重载和重写

​ c、对象的造型

5、抽象类

​ a、为什么需要抽象类

​ b、如何定义抽象类

​ c、抽象类的使用场景

6、接口

​ a、何为接口

​ b、接口的作用

​ c、如何使用接口

7、内部类

​ a、普通的内部类

​ b、匿名内部类


一、类的定义

1.1、类的定义

在Java语言中,class类是最为基本的单元。

类:其实就是针对某一类事物的抽象描述,是一个模板。
   其实就是针对一组相似的对象的共性进行的描述。
   
   提示!
   类 = 属性 + 方法

1.2、类的设计语法

类的修饰符   class   类名{
       
}   

说明:

1、类的修饰符:public 、缺省 、abstract 、 final

2、class:是定义类的关键字;

3、类名:符合标识符的定义规范(首字母大写,多个单词合并则每个单词的首字母大写【参考源码】);

4、{ } : 类的主体部分,称为“类体”;

从以上的语法分析中,我们可以看出,设计一个类的外壳的时候,我们只需要关注各种修饰符即可。

public 、 缺省(不写) : 属于权限修饰符;

public  class   MyClass{
    
}

说明:MyClass这个类可以在一个Java项目中的任意的位置进行使用;


class   MyClass{
    
}

说明:MyClass这个类只能是在同一个包package下可以进行使用;

在项目开发中,可以适当的借助于不同的权限修饰符,来针对我们的类进行隔离。

abstract 、 final :属于具有特殊含义的修饰符;

abstract :修饰一个类的时候,表示这个类是一个“抽象类”。

final :修饰一个类的时候,表示这个类是不允许被继承的(不允许作为父类)。

源码中的String类就是这样设计的:     源码  public final class String

注意!

abstract 和 final 是不能同时搭配使用的;

在Java语言中,有一个永恒成立的等式:

java类 = 属性  +  方法

换言之: 类 是由属性 和 方法所构成的 ,属性和方法都是定义在类的类体之中 。

1.3、类中的属性

属性:其实指的就是类中的成员变量(全局变量),是针对事物外部特征的描述;

全局变量分为2类:
              
    a-----静态变量;
                  
    b-----实例变量;

1.3.1、属性的定义语法

修饰符   [static]   数据类型  属性名称  [= 值];

属性的修饰符有哪些?

public :公共的;

缺省 : 同一个包下可以进行访问;

protected : 具有2层含义:
     
         a、同一个包下可以进行访问;
         
         b、具有继承关系的可以进行访问(不关系包的位置);

private :只能在本类中进行使用,跨类则不能访问;

----------------------------------------------------------

static : 修饰属性表示这是一个静态的变量;

final : 表示一个常量,必须要手动赋值;

transient : 修饰属性防止属性被序列化(后续讲解);

volatile : 属性同步的约定(涉及到Java多线程);

1.3.2、属性的访问

属性就是对象的外部特征的描述,我们在使用的过程中:

如果是静态的属性: 【类名.属性】名称 进行访问;

如果是实例的属性: 创建对象 , 再通过【对象.属性】进行访问;

1.4、类中的方法

方法:也称为“函数”,就是针对事物的功能进行的描述(表示能够干什么事情)。

     方法从静态性与否的角度进行分类:
        
           a、静态方法
           
           b、实例方法

1.4.1、方法的定义语法

修饰符   [static]   void|数据类型   方法名称(参数列表.....){
    //方法体;
    [return 值;]
}

方法的修饰符有哪些?

public :公共的;

缺省 : 同一个包下可以进行访问;

protected : 具有2层含义:
     
         a、同一个包下可以进行访问;
         
         b、具有继承关系的可以进行访问(不关系包的位置);

private :只能在本类中进行使用,跨类则不能访问;

-------------------------------------------------------------

static : 修饰的方法为静态方法;

final :修饰的方法为最终版本的方法,这种方法是不能被重写的;

synchronized : 修饰的方法为线程同步(后续讲解);

abstract : 抽象方法(抽象类的时候进行讲解);

native : 本地方法(这种方法是没有方法体的,内部使用的是非Java语言实现的,更多的是C和C++以文件的形式来实
          现的);

1.4.2、方法的调用

方法的分类(从静态与非静态的角度分): 静态方法 和 非静态方法;

静态方法:    【类名.方法名称】进行调用;

实例方法:    创建对象,   再【对象.方法名称】进行调用;

方法的分类(有无返回值的角度分):有返回值 和 无返回值;

有返回值 :    数据类型  变量  =  调用方法();

无返回值 :    调用方法();

方法的分类(有无参数列表角度分):有参数 和 无参数;

有参数的方法:    调用方法(实际值1,实际值2,....);

无参数的方法:    调用方法();

1.5、区分静态和非静态

加载时机不同

   1、一个类中的静态成员会在类加载的时候,首先进行初始化(创建并产生默认值);

   2、一个类中的实例成员会在创建类的对象的时候,才进行初始化;

生命周期不同

   1、静态的成员在类加载的时候创建,类被卸载的时候销毁,生命周期较长;
   
   2、实例成员在创建对象的时候初始化,对象销毁的时候一起销毁,相对较短;
共享性不同

   1、静态的成员是被这个类的所有的对象共享的;
   
   2、实例的成员是被每个对象独立的拥有;
   
访问方式不同

   1、静态的就是通过【类名.进行访问】;
    
   2、实例成员必须要创建对象,再通过【对象.进行访问】;

过渡!

我们为何要使用Java作者开发出来的那些类?

   答:因为这些类中包含了我们所需要的一些功能,我们可以不用编写Java程序代码而直接使用。
   
Java内部提供的所有的类,是不能完全满足我们的现实需求的,所以我们就需要自己定义出新的类。那么定义新的类的目
标是什么呢?

   答:如何使用这个类?需要创建类的对象来进行使用内部的属性和方法。
   

结论:定义类不是目的,最终是为了使用类中的属性  和 方法。

二、对象的定义

2.1、对象的定义

class类是具有抽象性的,是对某一类事物的描述,它是一个基础也是一个前提条件,也可以把它视为一个“模板”。我们

基于这个“模板”所产生出来的一个一个具体的东西,就称为“对象”。

对象是客观存在的一个一个的个体,是看得见摸得着的客观事物。

类:是抽象描述每一类事物------模板;

对象:是类的具体化,客观存在的实体-----具体化;

2.2、类的具体化

针对已经存在的某个类,如何将其进行具体化呢?也就是如何创建出具体的对象呢?

我们使用的是类中的【构造方法】来实现的;

class类-------【构造方法】--------对象

2.2.1、什么是构造方法

构造方法也是属于方法,就是在类中使用类的名称作为方法的名称,并且永远都是如此。而且是没有所谓的返回值的约定(连void都没有),因为构造方法不做别的事情,就是生成对象的。
public  class  Student{
    
    //构造方法
    public  Student(){
        
    }
}

2.2.2、构造方法的特点

构造方法是特殊的方法,存在如下的几个特点:

1、构造方法的名称永远和类的名称是一致的;

2、构造方法永远是没有返回值的约定的,连void都没有;

3、一个类中可以同时存在多个构造方法,参数上会存在差别,不同的构造表达了不同的初始化程度;
public class Student {
	
	//属性
	public  String name;
	
	public  int   age;
	
	public  char  sex;
	
	//无参数的构造方法
	public  Student(){
		
	}
	//带有1个参数的构造方法
	public  Student(String name){
		//打印的name是局部变量中的name的值
		//局部变量和全局变量重名的时候,遵循的是就近原则
		//System.out.println(name); // “刘德华”  而不是 null
		this.name=name;
		//当成员变量和局部变量重名的时候,我们使用的是this关键字来进行区分的
		//this代表的是当前类(Student)的对象
	}
	//带有3个参数的构造方法
	public  Student(String name,int  age,char sex){
		this.name=name;
		this.age = age;
		this.sex = sex;
	}		
}


Test.java

public class Test {
	
	public static void main(String[] args) {
		/*
		//创建1个学生对象
		Student  stu1 = new  Student();
		//属性都是默认值
		System.out.println(stu1.name);//null
		System.out.println(stu1.age);//0
		System.out.println(stu1.sex);//空白字符
		
		*/
		
		/*
		Student   stu2 = new  Student("刘德华");
		*/
		
		//使用同一个构造,创建多个对象
		Student   stu3 = new  Student("张学友",30,'男');
		System.out.println(stu3.name);
		System.out.println(stu3.age);
		System.out.println(stu3.sex);
		
		Student   stu4 = new  Student("张三丰",40,'男');
		System.out.println(stu4.name);
		System.out.println(stu4.age);
		System.out.println(stu4.sex);		
	}
}

说明!

1、调用构造创建对象的时候,使用的是new关键字;

2、选择使用不同的构造方法来创建对象,表达的是对当前这个对象不同程度的初始化;

4、我们调用构造方法的时候,使用的是 new 关键字;

5、当我们的一个类中没有手动定义构造的时候,系统会默认一个不带参数的构造,一旦出现手动定义的构造,系统就不再
   默认了。
   
6、一个类中同时存在的多个构造方法,就是构成了最为典型的“方法的重载”;   
2.2.2.1、方法的重载
在【同一个类中】,方法名称相同、参数列表不同(参数个数不同、参数类型不同、参数的顺序不同)的一系列方法,称为“方法的重载”;
/**
 * MyBean类中定义几个方法来实现某些功能
 * 
 * 演示方法的重载(MyBean类中的4个方法就构成了方法的重载)
 */
public class MyBean {
	
	// 任意2个整数的和
	public int getSum(int a, int b) {
		int sum = a + b;
		return sum;
	}

	// 任意3个整数的和
	public int getSum(int a, int b, int c) {
		int sum = a + b + c;
		return sum;
	}
	
	// 任意4个整数的和
	public int getSum(int a, int b, int c,int  d) {
		int sum = a + b + c + d;
		return sum;
	}
	
	public int  getSum(String name,int  a, int  b){
		return a+b;
	}

}


MyBeanTest.java

public class MyBeanTest {
	
	public static void main(String[] args) {
		
		MyBean   mb = new MyBean();
		//调用的以下这些方法,就是方法的重载
		mb.getSum(1, 2);
		mb.getSum(1, 2, 3);
		mb.getSum(1, 2, 3, 4);
		mb.getSum("admin",1, 2);	
	}	
}
2.2.2.2、方法重载的优势
在功能确定的情况下,最大化满足外界灵活多变的需求。  比如:Arrays.sort(各种类型的数组);

附加内容:可变参数的使用

//任意的多个整数之和,咋办?---使用数组定义
	/*
	 * public int getSum(int[]  is){
		int sum = 0 ;
		if(is!=null&&is.length>0){
			for(int value:is){
				sum+=value;
			}
		}
		return sum;
	}
	*/
	
	//使用可变参数来定义方法----可变参数
	public  int  getSum(int...  is){
		//在定义可变参数的时候,在参数列表中最多只能有1个,并且只能在列表的末端
		int sum = 0 ;
		if(is!=null&&is.length>0){
			for(int value:is){
				sum+=value;
			}
		}
		return sum;
	}
	

在方法进行调用的时候的差异对比:

        //首先需要将数据保存数组中
		int[]  is = {1,2,3,4,5,6};
		//才能使用这个方法来进行求和
		int sum = mb.getSum(is);	
		
		//使用可变参数后,的调用情况如下
		int sum = mb.getSum(1,2,3,4,5,6,7,8,9,10); //直接给出数据,不用在数组中进行封装,简单方	

2.2.3、构造方法的作用

1、基于类创建出对象;

2、基于创建的这个对象进行不同程度的初始化;

2.3、this关键字

this关键字永远表示的是 当前这个类的对象 的指代, 它是一个变量 ;
public class User {
	
	public  String  name;
	
	public  int   age;
	
	public   String  address;
	
	
	//自定义一个方法
	public  void   method1(){
		//通过user1.method1调用方法的时候,this指代的user1这个对象
		this.name = "admin";
		//通过user2.method1调用方法的时候,this指代的user2这个对象
		
		//总结:
		//不管是user1 还是 user2对象,都是属于User这个类的对象;
	}
    ...............
}	

UserTest.java

public class UserTest {

	public static void main(String[] args) {

		User user1 = new User();
		user1.method1();

		User user2 = new User();
		user2.method1();
	}
}

注意!

this关键字的使用场景:

1、用于在同一个类中进行重名的 局部变量 和 成员变量 的区分;

2、在同一个类中,this可以用于在【非静态的方法中】进行方法的互调;

​ 【this关键字是无法使用在静态的方法或者语句块中的】

3、在同一个类中,this可以进行构造的互调,并且只能是在第一行的位置;

三、类中的代码块

代码块:其实可以理解为一个没有名字的方法,也就是说属于“方法”的范畴。

在Java的类中,代码块可以分为2种形式:

   a、静态代码块
   
       作用:为类中的静态成员做提前的准备和初始化工作
   
   b、实例代码块
   
       作用:为类中的实例成员做提前的准备和初始化工作
package com.it.www.oop1;

public class User {
	
	//main方法是程序执行的入口----JVM来控制的
	//在main方法执行之前需要有哪些动作?-----  类的加载的过程
	public static void main(String[] args) {
		new  User();
	}
	
	//静态代码块-------1、自动执行的      2、何时被自动执行的? (执行的比较早)
	static{
		System.out.println("----static-----");
	}
	
	//实例代码块-------1、自动执行   2、在创建User类的对象的时候执行的
	{
		System.out.println("----instance----");
	}

}

提示!

不管是静态代码块还是实例代码块,都是属于方法。

四、完整的一个类的加载顺序

package com.it.www.oop1;

public class User {
	
	private   static    int  age = 30;
	
	private    String   name = "name";
	
	public  User(){
		System.out.println("----User----");
	}
	
	//main方法是程序执行的入口----JVM来控制的
	//在main方法执行之前需要有哪些动作?-----  类的加载的过程
	public static void main(String[] args) {
		new  User();
	}
	
	//静态代码块-------1、自动执行的      2、何时被自动执行的
	static{
		System.out.println("----static-----");
	}
	
	//实例代码块-------1、自动执行   2、在创建User类的对象的时候执行的
	{
		System.out.println("----instance----");
	}

}

总结:

1、在类被加载的过程中,所有的静态成员都被创建和初始化了。

2、在创建对象的过程中,才会针对所有的实例成员进行创建和初始化。

3、针对任意的成员变量,都会存在一个创建 (系统初始化值) + 初始化(手动赋值)。

五、继承关系

5.1、继承的基本概念

继承就是将一个类中的成员转移到另一个类中来进行使用,从而减少重复性代码的编写量(提高复用性)。
继承其实就是将许多个类中公共的部分进行提取,封装为一个较为基础性的class 类,作为其“父类”。让子类们通过extends继承来进行享用父类中的属性 和 方法。

5.2、继承定义语法

继承机制的实现,就是在类的后面使用extends关键字来进行定义:

public  class  Student   extends  Person{
     .......................
}

public  class  Teacher  extends  Person{
}

Student类:是子类;

Person类:是父类(基类);

注意!

在Java语言中【类与类之间】的继承,只能支持【单继承】。

5.3、继承案例

Person.java

package com.it.www.oop2;
/**
 * 父类(基类)----继承了Object类
 */
public class Person {
	
	public  String  name;
	
	public  int   age;
	
	public  char  sex;
	
	public   void   sleep(){
		System.out.println("人睡觉!");
	}
	
	public   void   eat(){
		System.out.println("人吃饭!");
	}
}

Student.java

package com.it.www.oop2;
/**
 * 子类-----继承的是Person类
 */
public class Student extends  Person{
	
    //通过继承来进行了替代
    /*public  String  name;
	
	public  int   age;
	
	public  char  sex;
	
	public   void   sleep(){
		System.out.println("人睡觉!");
	}
	
	public   void   eat(){
		System.out.println("人吃饭!");
	}*/
	
	//以下的属于学生特有的属性 和 方法
	public  int  sNo;//学号
	
	public  void  study(){
		System.out.println("学生学习!");
	}
}

Student_Test.java

package test;

import com.it.www.oop2.Student;

public class Student_Test {
	
	public static void main(String[] args) {
		//创建一个学生对象
		Student  student = new  Student();
		
		//使用的是父类Person中继承过来的属性和方法
		student.name="小花";
		student.sex='女';
		student.age = 25;
		
		student.eat();
		student.sleep();
		
		//使用的是Student自身特有的属性和方法
		student.sNo = 1001;
		student.study();
				
	}
}

备注!

在继承的过程中,并不是将父类中所有的属性和方法都能同时继承过来,取决于各种不同的修饰符的规则。

在继承的使用中,可以适当的使用不同的修饰符针对属性和方法进行保留。

在使用继承的过程中,需要注意的事项:

1、extends的实现,一定是依赖父子之间的构造方法的。

2、子类的构造中通过super来调用了父类中相应的构造方法来进行初始化(不能不对应,否则继承不成立)。

3、super就是用于在子类的构造中调用父类的构造,只能在是子类的构造方法的第一行,所以和this实现构造互调的时
   候,不能搭配使用。

5.5、super关键字

5.5.1、super关键字的使用场景

1、用于父子之间的构造方法中,实现构造的调用。

2、在父子类之间可以通过super来区分重名的属性 和 方法。

3、语法特点:super只能是使用在非静态的方法或者语句块中。

5.6、方法的重写

5.6.1、方法重写的定义

方法的重写:就是在父子类之间,方法名称、参数列表都相同的方法,称为“方法的重写”,方法的重写也讲为:“方法的覆写”、"方法的改写"。

5.6.2、方法重写案例

Person.java

package com.it.www.oop3;
/**
 * 父类(基类)----继承了Object类
 */
public class Person {

    ..............
    
	public   void   sleep(){
		System.out.println("人睡觉!");
	}
	

	public   void   eat(){
		System.out.println("人吃饭!");
	}
}

Student.java

package com.it.www.oop3;

/**
 * 子类-----继承的是Person类
 */
public class Student extends Person {

    ..........
    //重写的父类中的eat方法 
	public   void   eat(){
		System.out.println("人吃饭----学生在食堂吃饭!");
	}

}

Student_Test.java

        //学生  是  学生
		Student  student = new  Student();
		student.eat(); // "人吃饭----学生在食堂吃饭!"
		
		//学生  是   人
		Person   stu = new  Student();

        //学生  是   对象
        //Object   nnewStu = new  Student();

		//对象接收的类型,就是对象使用过程中可以访问的数据;
		stu.sleep();  //执行的就是父类中的sleep方法。---"人睡觉!"
		stu.eat(); // "人吃饭----学生在食堂吃饭!"
		//eat方法是来自于父类中的,但是实际执行的是子类中重写后的eat的方法。

		//总结:
		//儿子充当父亲去使用的时候。子类中重写后的方法会在父类的方法调用的时候进行执行;

        //stu.study();【错误的】 此方法是没有的。
		//因为在儿子充当父亲去使用的时候,儿子自身的特性丢失。

        //对象类型的强制转换(对象的造型)
		Student  newStu = (Student)stu;

Student  student = new  Student();

Person   stu = new  Student();

同样是一个Student类型的对象,存在了多种形式的类型。

附加知识点!

User.java

package com.it.www.bean;

public class User {
	
	
	public  String name;
	
	public   int  age;

	public User() {
		super();
	}

	public User(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	//重写了Object类中的toString方法
	public String toString() {
		return "User [name=" + name + ", age=" + age + "]";
	}

}

UserTest.java

package test;

import com.it.www.bean.User;

public class UserTest {
	
	public static void main(String[] args) {
		
		User user = new User("admin",30);
		
		//打印输出(地址)----默认的调用的是toString方法
		System.out.println(user); //com.it.www.bean.User@15db9742
		//重写toString方法后的结果:User [name=admin, age=30]				
	}	
}

需求:

User 对象 user1===== [name="admin",age=30]

User对象 user2===== [name="admin",age=30]

user1 和 user2 是两个不同的对象,但是数据信息又是相同的。我们想比较出2个对象中的数据是否相同?

答:重写类中的equals方法,实现的是内容信息的比较即可。

//重写父类的equals方法-----实现内容信息的比较
public boolean equals(Object obj) {
		
		if(this==obj){
			return true;
		}
		
		if(obj instanceof User){
			
			User user = (User)obj;
		//this.name中的equals方法,就是String类中的equals方法,作者已经重写过了,实现的内容的比较
			if(this.name.equals(user.name)&&this.age==user.age){
				return true;
			}
		}
		return false;
	}

注意!

Object 类中的equals方法实现的是对象的地址的比较。

String类中的equals方法已经重写过了,实现的是内容信息的比较。

将来自定义的类中的equals方法比较的是什么?取决于自己是否进行重写。

5.6.2、方法重写的原则

1、方法的重写一定是建立在父子类之间的。

2、所有的静态的方法是没有重写的概念,仅仅只是覆盖。

3、重写后的方法的【方法名称、参数列表必须要相同】。

3、子类中重写后的方法不能缩小其访问权限(可以相同或者更大)。

4、子类中重写后的方法不能产生更为严格的异常。

5.6.3、重写和重载的区别

使用范围:
   重写是在父子类之间,重载是在同一个类中。
   
方法的要求:
   重写必须要保证方法名称、参数列表都一致,重载是方法名称一致,参数列表一定要不同。  

Java面向对象最为显著的三大特征:继承 、 封装 、 多态。

多态性:就是指方法的重写和方法的重载。

六、抽象类

6.1、抽象类的基本概述

类本身就具有抽象性,是针对某一类事物的描述。

抽象类:抽象类肯定比类更为抽象。描述的事物的信息是不足的。不能产生出具体的对象。

抽象类是不能实例化(不能创建出对象的),但是存在构造方法。
针对现实生活中的某些事物的描述,我们发现具备某些功能,但是又无法实现,这种功能我们会以抽象方法的形式来进行定义(只说有这个功能,但是无法实现,没有方法体部分)。

从Java语言语法的角度上,只要含有了抽象方法的类,一定要声明为“抽象类”才可以。

抽象类中的抽象方法,只是定义了功能,没有任何的实现。这些方法对于我们而言,毫无价值。所以,抽象类是不允许创建对象的。
package com.it.www.bean;

//描述形状(平面性)的类
public abstract class Shape {
	
	public String  name;
	
	//计算周长----抽象方法
	public abstract  double  getPerimeter();
	
	
	//计算面积----抽象方法
	public abstract double  getArea();

}

6.2、抽象类的使用

Circle.java

package com.it.www.bean;
/**
 * 描述圆形的类
 * @author Administrator
 */
public class Circle extends  Shape{

	public  double  r;  //半径
	
	public double getPerimeter() {
		
		return 2*Math.PI*r;
	}

	public double getArea() {
		
		return Math.PI*r*r;
	}


}

Square.java

package com.it.www.bean;
/**
 * 描述矩形的类
 * @author Administrator
 *
 */
public class Square extends Shape{

	
	public double getPerimeter() {
		return 0;
	}

	public double getArea() {
		return 0;
	}

}

提示!

抽象类作为一个最高的层次出现,旗下划分出很多的子的扩展。也就是说构建出了一个总分的层级关系。

总:针对其所有的子类的共性的提取。

子:针对总定义的规则来进行具体的实现(重写方法)。

Test.java

package com.it.www.bean;

public class Test {
	
	public static void main(String[] args) {
		
		Circle  c = new Circle(10);
		c.name = "圆形";
		
		double  per = c.getPerimeter();
		double  area = c.getArea();
		
		System.out.println(c.name+"-----"+per+"-----"+area);
		
		//接收的类型是   (抽象类)
		Shape   c1 = new  Circle(20);
		System.out.println(c1.getPerimeter()+"******"+c1.getArea());		
		
	}

}

注意!

所有的抽象类最终都是通过extends继承的方式来扩展出子类,从而最终进行使用的。

abstract 和 final 是不同搭配使用的。

6.3、抽象类的特点

1、抽象类使用的是abstract进行修饰的;

2、抽象类是存在构造的,但是不能创建对象(信息不足);

3、抽象类最终都是通过继承扩展子类来进行最终的使用;

4、含有抽象方法的类,一定要被声明为抽象类;

5、抽象类不一定含有抽象的方法;

抽象类也是一种复合引用类型。

类 可以继承 类

抽象类 可以继承 类

类 可以继承 抽象类

抽象类 可以继承 抽象类

6.4、抽象方法

抽象方法是使用在抽象类 或者 接口中的。

抽象方法是没有方法体的,只是说明具备某种功能,但是无法具体的实现。

抽象方法的语法格式:

修饰符   abstract   void/数据类型   方法名称(参数列表...);

注意!

修饰符是不能使用private的,因为无法实现继承。

6.5、抽象类的总结

抽象类大家可以这么进行理解:

在编写代码的层面上,就是将许多的类的共性进行进一步的抽取,并且进行封装的过程。

实体类------提取的是-------许多个对象的共性。

抽象类------提取的是-------许多个类的共性。

七、接口

7.1、接口基本概念

接口:其实就是一种规范,全部都是只说不干。接口的内部只允许存在 常量  和  抽象方法。通常是适用于在软件系统设计中进行模块之间的通信规范的编写。

7.2、接口的定义语法

public|缺省   interface    接口名称{
    //定义常量: 
    //public  static  final  数据类型   常量名称 = 值;
    
    //抽象方法:
    //public  abstract  void/数据类型   方法名称(参数列表...);
}

注意!

接口是没有构造方法的,更不能创建对象。只是定义一种规则和规范。

package com.it.www.oop1;
/**
 * 介绍接口的语法格式
 * @author Administrator
 *
 */
public interface InterfaceA {
	
	public static final  String  NAME="admin";
	
	//在接口中定义常量的格式为:public static final
	//如果我们在定义的时候存在相应的省略,默认的也是 public static final
	public static   String  NAME1="admin";
	
	public    String  NAME2="admin";
	
	
	//所以我们通常在开发的过程中,为了相应的简化写法,一般是这样做的。
	String  NAME3="admin";
	
	
	//抽象方法:默认的修饰符是 public   abstract 
	public  abstract  void   method1(String name);
	
	//通常在开发中为了简化,可以这样写:
	void  method2(String name);	
}

7.3、接口使用

InterfaceB.java

package com.it.www.oop1;

public interface InterfaceB {
	
	//分页单位为 5
	int  PAGE_SIZE = 5;
	
	
	int   getPageSize();
	
	int   getPageData();

}

MyClass.java 实现类

package com.it.www.oop1;

/**
 * MyClass是一个类,这是实现InterfaceB这个接口的一个实现类。
 * @author Administrator
 */
public class MyClass implements InterfaceB {

	public int getPageSize() {
		//可以使用 this.PAGE_SIZE来进行使用,说明PAGE_SIZE已经是MyClass类中的常量了,所以,接口有这么一个规定
		//只要是这个接口的实现类,都可以毫无条件的共享接口中的全部常量。
		return this.PAGE_SIZE;
	}

	public int getPageData() {
		
		return 0;
	}

}

注意!

1、定义接口使用的是interface关键字;

2、接口中只有常量和抽象方法2部分;

3、实现类来实现接口使用的是implements关键字;

4、实现类一旦实现了某个接口,就可以毫无条件的共享接口中的所有常量;

InterfaceC.java

package com.it.www.oop1;

public interface InterfaceC {
	
	String  NAME = "刘德华";
	
	String   getName();
	
	int    getAge();

}

MyClass.java

package com.it.www.oop1;

/**
 * MyClass是一个类,这是实现InterfaceB这个接口的一个实现类。
 * @author Administrator
 */
public class MyClass implements InterfaceB , InterfaceC{

	public int getPageSize() {
		//可以使用 this.PAGE_SIZE来进行使用,说明PAGE_SIZE已经是MyClass类中的常量了,所以,接口有这么一个规定
		//只要是这个接口的实现类,都可以毫无条件的共享接口中的全部常量。
		return this.PAGE_SIZE;
	}

	public int getPageData() {
		
		return 100;
	}

	
	public String getName() {
		
		return this.NAME;
	}

	public int getAge() {
		return 30;
	}
}

提示!

同一个类可以同时实现多个接口,多个接口之间使用【,】号进行分隔;

InterfaceD.java

package com.it.www.oop1;
/**
 * 接口之间也是可以继承的,并且可以支持多重继承。
 * @author Administrator
 */
public interface InterfaceD  extends InterfaceB,InterfaceC {
	
	void   method01();
	
	void   method02();

}

MyClass1.java

package com.it.www.oop1;

/**
 * 实现类中需要实现的方法就包含了IntefaceB 、 C 、 D中的所有的抽象方法 和 共享了所有的接口中的所有常量
 * @author Administrator
 */
public class MyClass1 implements InterfaceD {

	public int getPageSize() {
		// TODO Auto-generated method stub
		return 0;
	}

	public int getPageData() {
		// TODO Auto-generated method stub
		return 0;
	}

	public String getName() {
		// TODO Auto-generated method stub
		return null;
	}

	public int getAge() {
		// TODO Auto-generated method stub
		return 0;
	}

	public void method01() {
		// TODO Auto-generated method stub

	}

	public void method02() {
		// TODO Auto-generated method stub

	}
}

提示!

1、接口之间是可以继承的,并且可以支持多重继承;

2、独立的接口就是某一类独立的操作,不同的接口可以同时作用(implements)在需要的类上;

3、弥补了Java中类只支持单继承的局限性;

7.4、接口的特点

1、定义的时候使用的是interface关键字;

2、接口就是用于负责定义功能(规范 或 标准);

3、接口中是没有构造方法的;

4、接口都是通过实现类使用implements 来进行实现的;

5、接口中只有常量 和  抽象方法;

6、接口之间是可以进行继承的,并且可以支持多重继承;

7、接口重点在于一种设计中的使用(主要是针对模块 、 系统)之间来进行衔接的;

7.5、抽象类和接口的区别

抽象类与接口在语法上的区别

1.抽象类里可以有构造方法,而接口内不能有构造方法。
2.抽象类中可以有普通成员变量,而接口中不能有普通成员变量。
3.抽象类中可以包含非抽象的普通方法,而接口中所有的方法必须是抽象的,不
  能有非抽象的普通方法。
4.抽象类中的抽象方法的访问类型可以是public ,protected和默认类型,但接
  口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
5.抽象类中可以包含静态方法,接口内不能包含静态方法。
6.抽象类和接口中都可以包含静态成员,抽象类中的静态成员变量的访问类
  型可以任意,但接口中定义的变量只能是public  static  final类型,并且默认为
  public  static  final类型。
7.一个类可以实现多个接口,但只能继承一个抽象类。
8.抽象类和接口所反映出的设计理念不同。其实abstract class表示的是“is-a”
  继承关系,interface表示的是“like-a”关系。

两者在应用上的区别

 接口更多的是在系统框架设计方法发挥作用,主要定义模块或者系统之间的通信,而抽象类主要在代码实现方面发挥作
 用,可以实现代码的重用。

7.6、面向接口编程模式

com.it.www.dao : 定义数据库操作的接口包;

com.it.www.daoi : 定义数据库操作的接口的实现包;

com.it.www.service : 定义项目业务操作的接口包;

com.it.www.servicei : 定义业务操作的实现包;

PersonDao.java 接口

package com.it.www.system;

public interface PersonDao {
	
	void  savePerson();
	
	void  queryPersons();

}

PersonDaoImple.java实现类

package com.it.www.system;

public class PersonDaoImple implements PersonDao {

	//实现的是接口中的方法
	public void savePerson() {
		System.out.println("---save Person----");

	}

	public void queryPersons() {
		System.out.println("----query  persons----");
	}
	
	//自己这个类的方法
	public  void   myMthod01(){
		System.out.println("----customer 1-----");
	}
	
	public  void   myMthod02(){
		System.out.println("----customer 2-----");
	}

}

Test.java

package com.it.www.system;

public class Test {
	
	public static void main(String[] args) {
		
		//使用 实现类的类型  创建  实现类的对象
		PersonDaoImple   personDaoImple = new  PersonDaoImple();
		
		personDaoImple.savePerson();
		personDaoImple.queryPersons();
		personDaoImple.myMthod01();
		personDaoImple.myMthod02();
		
		//使用面向接口编程模式
		
		//使用接口的类型  接收  实现类的对象
		PersonDao    personDao = new  PersonDaoImple();
		
		//是PersonDao的类型,就只能调用出PersonDao中定义过的方法。
		personDao.queryPersons();
		personDao.savePerson();
		
		//实现类PersonDaoImple中的自己的方法丢了。被接口给过滤了,所以,接口永远对外提供的功能都只是接口中定义过的功能。
	
		
		
	}

}

提示!

更多的基于接口的分层结构和面向接口的编程模式,见: day05_09 项目中;

八、内部类

8.1、内部类的基本知识

内部类:就是定义在一个类的内部的类。内部类就是专门负责独立的描述某一类事物,而这一类事物仅仅只是在当前这个所

在类的范围内进行使用。在类中的任意位置需要提供局部性的服务,就可以使用内部类来实现。

问题1:
   
   可以定义在一个指定的类内部的哪些位置?
   
   答:任意位置(直接在类体中、方法中、代码块中、语句块中)。
   
问题2:

   为什么要使用内部类?
    
   答:就是为了在一个类的内部的任意的位置提供局部性的服务。

8.2、内部类的定义

package com.it.www.oop1;

public class OuterClass {

	// 类体中直接定义
	protected class InnerClass1 {

	}
	
	//在代码块中进行内部类的定义
	{
		class InnerClass4 {

		}
	}

	// 方法中定义内部类
	public void method1() {

		// InnerClass2只是能在method1方法中提供服务
		class InnerClass2 {
			public void tt() {
				System.out.println("--InnerClass2-tt--");
			}
		}

		// 局部变量
		InnerClass2 ic2 = new InnerClass2();

		ic2.tt();

	}

	public void method2() {

		if (true) {
			//这个内部类定义在语句块中,所以仅仅只能是在这个语句块中进行使用
			class InnerClass3 {
				public void test() {
					System.out.println("--InnerClass3-test--");
				}
			}
		}

	}

}

提示!

1、在定义内部类的时候,可以理解为定义所在类的成员;

2、内部类能够使用的各种修饰符是会随着使用的位置发生变化,而且和普通的类是不一样的;

3、在类的内部哪里需要就在哪里进行定义和使用,提供的都是局部性的服务;

8.3、匿名内部类

8.3.1、基础简介

匿名内部类:就是没有名字的内部类。

8.3.2、实现语法

InterfaceA接口

package com.it.www.oop1;

public interface InterfaceA {
	
	void  t1();
	
	void  t2();

}

MyAbstractClass抽象类

package com.it.www.oop1;

public abstract class MyAbstractClass {
	
	
	public  abstract  void  method1();
	
	public  abstract  void  method2();

}

MyOuterClass 定义匿名内部类的类

package com.it.www.oop1;

public class MyOuterClass {
	
	//我们创建的是 InterfaceA 接口的一个没有名字的实现类的对象  myIf
	InterfaceA  myIf =  new  InterfaceA() {
		
		public void t2() {
			System.out.println("----t2----");
		}
		
		public void t1() {
			System.out.println("----t1----");
		}
	};
	
	
	//new的是MyAbstractClass这个抽象类的子类的对象,这个子类是没有名字的。
	MyAbstractClass   mc = new  MyAbstractClass() {
		
		public void method2() {
			// TODO Auto-generated method stub
			
		}
		
		public void method1() {
			// TODO Auto-generated method stub
			
		}
	};
	
}

匿名内部类使用的场景总结:

1、接口或者抽象类中的方法较少;

2、项目中最好是存在差异化的使用(比如:同一个接口的方法在不同的地方需要进行不同的实现);

3、哪里需要就在哪里进行特殊化的处理;

8.3.3、匿名内部类的使用

User.java

package com.it.www.beans;

public class User {
	
	public   int   id;
	
	public   String   name;
	
	public   int    age;

	public User() {
		super();
		// TODO Auto-generated constructor stub
	}

	public User(int id, String name, int age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}

	public String toString() {
		return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
	}
}

Test.java

package com.it.www.oop2;

import java.util.Arrays;
import java.util.Comparator;

import com.it.www.beans.User;

public class Test {

	public static void main(String[] args) {
		Test test = new Test();
		test.mySort2();
	}

	// 针对可以排序的数组进行排序的处理
	public void mySort1() {
		int[] is = { 1, 6, 3, 5, 2, 7, 9 };
		Arrays.sort(is);
		System.out.println(Arrays.toString(is));
	}

	// 针对User类型的对象数组进行排序
	public void mySort2() {

		User user1 = new User(1, "admin", 26);
		User user2 = new User(2, "guest", 30);
		User user3 = new User(3, "tom", 24);
		User user4 = new User(4, "lucy", 20);

		User[] users = {user1,user2,user3,user4};
		
		//查看数据
		for(User user:users){
			System.out.println(user);
		}
		
		//需要将User数组中的对象进行排序的处理------问题?(缺乏排序的比较标准)
		//Arrays.sort(users);
		
		//使用匿名内部类方案来实现比较器  (推荐使用匿名内部类的使用方案)
		Arrays.sort(users,new Comparator() {
			//按照User类中的age属性来进行排序的处理
			public int compare(Object o1, Object o2) {
				
				if(o1 instanceof User && o2 instanceof User){
					
					User  user1 = (User)o1;
					User  user2 = (User)o2;
					
					return  user1.age - user2.age;
					
				}
				return 0;
			}
		});
		
		
		//第二种:自定义实现类的方案来完成  (不推荐的做法)
		//局限性很多:1、极为不通用       2、会产生出大量的比较器实现的类
		/*MyComparable  mc = new  MyComparable();
		Arrays.sort(users,mc);*/
		
		System.out.println("----------------------------");
		//查看数据
		for(User user:users){
			System.out.println(user);
		}
		

	}

}

非匿名内部类实现方案(不推荐)

package com.it.www.oop2;

import java.util.Comparator;

import com.it.www.beans.User;

/**
 * 在不使用匿名内部类的情况下,进行比较器的实现
 * 存在的潜在问题很多.....
 */
public class MyComparable implements Comparator {

	public int compare(Object o1, Object o2) {

		if (o1 instanceof User && o2 instanceof User) {

			User user1 = (User) o1;
			User user2 = (User) o2;
            
			return user1.age - user2.age;

		}
		return 0;
	}

}

九、标准JavaBean规范

JavaBean的定义:就是一个class类。

标准的JavaBean的定义:其实就是在JavaBean的基础上设置了一些规则和规范。

1、所有的属性都是private修饰的;

2、所有的属性的名称的首字母一定是小写的;

3、所有的属性都会对应一个公共的set(赋值) 和  get(取值)方法;

4、一般会添加上一个无参数的构造和一个全部带参数的构造;

注意!

以后我们会学习到Java的反射机制,反射机制中都会遵循标准JavaBean的规范来进行处理。

package com.it.www.entity;

public class User {
	
	private   String  name;
	
	private   String  address;
	
	private   int   age;
	
	private    char  sex;

	public User() {
		super();
	}

	public User(String name, String address, int age, char sex) {
		super();
		this.name = name;
		this.address = address;
		this.age = age;
		this.sex = sex;
	}

	//get方法是有返回值的,返回类型就是属性的类型,是没有参数的
	//get+属性名称的首字母大写+后面的所有的字符保持不变
	public String getName() {
		return name;
	}
	
	//属性:  Name
	//set方法名称:  setName
	//根据set方法名称还原属性名称
	//Name------name

	//set方法是赋值的,参数的类型就是属性的类型,是没有返回值的
	public void setName(String name) {
		this.name = name;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public char getSex() {
		return sex;
	}

	public void setSex(char sex) {
		this.sex = sex;
	}

}

十、单例设计模式

10.1、单例的概念

单例:就是多次获取得到一个指定的类的对象,但是最终都是同一个对象。有利于节约内存空间。

10.2、单例的实现

实现方案一:(饿汉式)

package com.it.www.oop;
/**
 * 饿汉式
 */
public class Person {
	
	//在类进行加载的时候,就已经把对象生成了(对象生成的事件较早)
	private   static    Person   person = new  Person();
	
	//将构造进行私有化
	private  Person(){
		
	}
	
	//对外提供对象
	public   static   Person   getInstance(){
		return person;
	}
}

实现方案二(懒汉式):

package com.it.www.oop;

/**
 * 懒汉式
 */
public class Student {

	private static Student student;// null

	private Student() {

	}

	// 此写法必须要将获取实例的方法设置为synchronized线程同步。
	public synchronized static Student getInstance() {

		if (student == null) {
			student = new Student();
		}
		return student;
	}
}

注意!

使用懒汉式的方式实现单例模式,必须要将提供实例的方法设置为线程同步synchronized;

posted @   疏影橫斜水清淺  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示