Java学习日记(三)

179 阅读15分钟

Java入门(三)

继承 & 重写 & 多态 & 拆装箱

#20190807

一、继承

1、继承是面向对象的三大特征之一

2、概念:

  • 通过从现有类中继承其属性和方法来充实自身,这种行为或现象被称为继承。新类被称为子类(扩展类、派生类),现有类被称为父类(基类、超类)。

3、继承最基本的作用:

  • 使代码可以重用,增加软件可扩展性

4、java中只支持单继承,即一个类只能有一个直接父类

5.关键字:extends

语法规则:

[访问修饰符] class 子类名 extends 父类名{
		//类的属性
		//类的成员方法
}
  • 访问修饰符如果是public,那么该类在整个项目里可见;
  • 不写访问修饰符,则为默认值,该类只在当前包里可见

6.特点:

  • 子类可以继承父类里public和protect修饰的属性和方法(不论子类是否与父类同包)

  • 子类可以继承父类里用默认访问修饰符修饰的属性和方法(子类与父类必须同包)

  • 子类不能继承父类的构造方法

  • 子类可以重写父类的方法

  • 继承提高了类间的耦合度

  • 子类可以扩展

7.继承和组合的区别:

​ 继承是is关系,组合是has关系

二、重写

1.概念:

  • 子类继承父类的方法后不能满足现有需求,可以在子类内部声明一个相同(包括返回值)的方法,这种方式称为重写
2.重写可以在方法前加@Override
  • 这样可以判断该方法是否是重写

3.重写满足的条件:

  • 必须发生在继承中
  • 子类中和父类相同的方法(方法名、参数、返回值)
  • 访问修饰符范围只能扩大,不能缩小

4.比较重载和重写

重载重写
发生在同一个类中发生在继承中
方法名相同、参数个数、类型、顺序不同方法名、参数、返回值都相同
与返回值无关返回值与被重写方法相同

相同点:

​ 都可以用super/this.变量名/方法名,调用属性和方法。(super指父类,this指当前类的成员变量/方法)

5.注意:

  • 在重写父类方法时要注意,返回值与父类方法不一致会报错
  • 子类中有方法名、返回值与父类某方法一致但参数不同时,该子类中的方法不是重写方法,是新的方法(可以用@Override判断)
  • 子类不能继承父类的构造方法,所以自然也不能重写

三、重写equals和hashCode

1.要求:

  • 自反性:x.equals(x)一定等于true,即自己一定等于自己
  • 对称性:x.equals(y) = true,则y.equals(x) 也是true
  • 传递性:x.equals(y) = true,y.equals(z) = true,则x.equals(z) 也等于true
  • 一致性:对任意对象,只要不改变,不管调用多少次结果都一样
  • 对于非空引用x,x.equals(null)一定是false

2.equals和hashCode

重写equals后一定要重写hashCode方法。

  • equals返回true,hashcode值一定相等

  • equals返回false,hashcode不一定相等

  • hashcode相同,equals不一定是true

  • hashcode不相同,equals一定是false

  • hashcode多少次调用,返回的值都要相同

  • //重写equals方法
    @Override
    	public boolean equals(Object obj) {
            if (obj == null)	//对于null返回false
    			return false;
    		if (this == obj)	//地址相同为同一对象
    			return true;
    		if (getClass() != obj.getClass())		//判断是否是同一个类
    			return false;
    		Department other = (Department) obj;	//转换类型
    		if (manager == null) {
    			if (other.manager != null)
    				return false;
    		} else if (!manager.equals(other.manager))
    			return false;
    		return true;
    	}
    
  • //重写hashcode方法
    @Override
    	public int hashCode() {
    		final int prime = 31;
    		int result = 1;
    		result = prime * result + ((manager == null) ? 0 : manager.hashCode());
    		return result;
    	}
    

3、instanceof

变量 instanceof 类型
  • 判断一个变量所指向的实例是不是指定类型的(判断对象是否属于某个类),如果是null,返回false
  • 使用instanceof/getClass() 重写equals,根据需求的不同选择不同,如果是按照父类的标准来判断是否相等,就用instanceof(obj instanceof 某类)。如果是按照各种的标准判断就用getClass()

四、super

  • 代表继承中的父类,它可以调用父类中的属性和方法(包括构造方法)。
  • 在子类中,如果没有声明调用父类无参构造,编译器会自动调用父类无参构造(super())。
  • 如果父类没有无参构造,就需要我们手动调用父类的有参构造。子类的构造方法中必须调用父类的构造,先有父类才有子类。
  • super只能出现在子类的普通方法或构造方法中,而不能是其他位置。
  • super不能访问父类的private成员。
  • super在子类构造方法中必须放在子类构造方法的首行

super和this的比较

superthis
调用父类的属性或方法调用自身的属性或方法
super不可以调用private可以调用自身private修饰的

五、Object类(P143)

  • 是所有类的父类,没有使用extends时,这个类就是直接继承Object类;

  • 所有对象都继承这个类的方法

  • 关系图

native

  • Java平台有个用户和本地C代码进行互操作的API,称为Java Native Interface (Java本地接口)。

六、多态

1.概念:

  • 多态是面向对象的三大特征之一
  • 它意味着一个特定类型的变量,可以引用不同类型的对象,并且能自动地调用引用的对象的方法。
  • 重写是实现多态的基石
  • 如果实现类型是子类类型,子类重写了父类方法,则调用的是子类的方法。如果子类未重写父类方法,则调用父类方法。

2.向上转型

  • 子类向父类转称为向上转型,不用强制转换
  • Pet pet = new Dog(); //父类引用子类对象

3、向下转型

  • 将一个指向子类对象的父类引用赋值给一个子类的引用,即将父类转换为子类类型,称为向下转型,必须进行强制转换

  • 语法规则:
    <子类类型><引用变量名>=(<子类类型>)<父类类型的引用变量>;
    

    例如:Dog dog = (Dog)pet; //父类引用子类对象

  • 如果像下面这样会报错

    Pet pet = new Dog();

    pet.catchingFlyDisc();//无法调用子类新增的方法

4、多态的应用:

  • 可替换性:多态对已存在的代码具有可替换性
  • 可扩充性:增加新的子类不会影响已存在的类的多态性、继承性、以及其他特性的运行和操作。
  • 接口性:多态是父类向子类提供一个共同的接口,由子类具体实现
  • 灵活性:多态提供灵活多样的操作
  • 简化性:多态简化了应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤其突出和重要。

七、包装类

1、装箱和拆箱:

  • 由基本类型转向对应的包装类转换称为装箱,例如:把int包装称integer类的对象;
  • 包装类向对应的基本数据类型转换称为拆箱,例如:把integer类的对象重写简化为int。

2、自动拆箱和装箱

  • 在jdk1.5之前必须手动拆箱装箱;
  • 在jdk1.5之后,系统将自动进行基本数据类型和对应包装类转换。

八、封装、继承、多态

1、封装

  • 我认为封装是把一个类的具体实现细节隐藏起来,外部只能通过调用类的方法才能访问到隐藏的信息

2、继承

  • 我认为继承是一个类继承了另一个类中除了private和构造方法外的所有方法,这个类就是子类,被继承的类就是父类;子类只能有一个父类,并且子类可以扩展,子类在继承父类的时候可以重写父类的方法。

3、多态

  • 我认为多态是一个事物多种形态,在Java中指同一个对象可以调用不同引用类型,根据引用类型的不同自动调用相应方法;重写是多态的基础,因为重写中除了方法体不同其余都和父类方法相同,调用时通过引用对象(实现类型)来判断要调用哪个方法

心得体会

​ 感觉这章学得不是很好,一句句的概念都知道,但是脉络好像没有捋清楚;在重写equals和hashCode方法那里比较不理解,为什么要重写equals,以前没有重写不是也在用吗?是因为以前用的时候是在比较字符串,而现字在要比较对象,所以要重写equals吗?

  • 其实是因为String里已经重写了equals,而其他的没有

抽象方法类和接口

#20190808

一、抽象方法

1、关键字:abstract

2、语法格式:

[访问修饰符]abstract<返回类型><方法名>([参数列表]);

3、解析:

  • 抽象方法没有具体的实现(没有方法体),只声明;
  • 抽象方法需要通过在其子类中重写方法才能实现(抽象方法在抽象类的子类里实现);
  • 抽象方法必须定义在抽象类中
  • 构造方法、static/final修饰的方法不能声明为抽象方法,但是抽象类有构造方法;
  • **private修饰符不能用来修饰抽象方法,**因为子类无法继承并重写父类的私有方法。

二、抽象类

1、语法格式:

abstract class<类名>{
    
}

2、解析:

  • 抽象类可以包含各种类型成员,包括普通方法和抽象方法
  • 抽象类不能被实例化,因为抽象方法没有方法体,抽象类不是完整的类,所以不能实例化。
  • 如果子类不是抽象类,就必须重写抽象类中所有抽象方法
  • abstract不能和final一起用,即final不能用在抽象类中

3.抽象类和方法的优势

  • 抽象类中已经实现的方法可以被其子类使用,实现了代码复用;
  • 同时,抽象类提供了抽象方法,保证了子类具有自身的独特性。
局限性:
  • 有时会造成代码冗余的问题

三、接口

1、概念:

  • 如果抽象类中的所有方法都是抽象方法,就可以使用接口来表示
  • 接口可以看作是特殊的“抽象类”,但是两者的设计理念和语法表示不同
  • 接口是一个不能实例化的类型

2、语法格式:

public interface 接口名{

}

3、解析:

  • 定义接口使用interface修饰符
  • 接口的访问权限是public或默认权限
  • 一个接口可以继承其他接口,称为父接口
  • 可以继承父接口中声明的常量和抽象方法

4.接口的成员列表

  • 声明形式
[public][static][final]数据类型 成员变量 方法名称(参数列表);
  • 接口中的方法默认都是public、abstract的,因此public、abstract可以省略。

  • 接口也必须通过子类实现

  • 实现接口

    public 类名 implements 接口名{}
    
    • 实现接口使用implements关键字
    • 一个类可以实现多个接口,各接口之间用逗号分隔
    • 实现接口的类必须实现接口中定义的所有抽象方法
    • 接口的实现类允许包含普通方法
    • **在实现抽象方法时要指定public权限,**否则会产生编译错误(接口里方法可以去掉修饰符,但实现方法的时候不能)

四、复杂接口

  • 接口本身也能继承接口

    • 语法格式:

    • [修饰符]interface 接口名 extends 父类接口1,父类接口2,……{
      }
      
  • 一个普通类只能继承一个父类,但能同时实现多个接口,也可以同时继承抽象类和实现接口

    • 语法格式:

    • class 类名 extends 父类名 implements 接口1,接口2,……{
          类的成员
      }
      
  • JDK8.0中接口的特性:

    • 引入了default关键字用于在接口中修饰方法(默认方法),default修饰的方法可以包含方法体
    • 接口中的default方法可以被子类覆盖,子类不覆盖的时候直接调用默认方法。
    • 新增了static方法(静态方法),可以使用接口名直接调用。

心得体会

​ 有没有感觉笔记越来越少了,没错,我越来越水了。。。。


内部类&匿名类

#20190809

一、内部类

  • 内部类是指一个类定义在另一个类的内部

  • 内部类分为:成员内部类、静态内部类、方法内部类

1、成员内部类

  • 类B在类A中声明,B就是A的内部类。

  • 对成员内部类使用java编译此文件,会产生两个class文件:A.classA$B.class

  • 例如:

    public class A{
    	private String a = "外部类变量";
    	public void a() {
    		B c = new B();
    		c.b();//调用内部类方法
    		System.out.println("外部类方法\t" + c.b);
    	}
    	//定义内部类
    	public class B{
    		String b = "内部类变量";
    		void b() {
    //			A b = new A();
    //			 b.a();//调用外部类方法
    			System.out.println("内部类方法\t" + a );
    		}
    	}
    }	
    	
    
  • 解析:

    • 成员内部类可以调用外部类的成员方法和成员变量(包括用pivate修饰的
    • 外部类不能直接调用内部的方法或变量,要用 new 内部类名.方法名/变量名
    • 成员内部类中不能定义不是final修饰的静态变量(final修饰的变量,变量名全部大写)
    • 可以使用this.变量名 调用变量,调用外部类的变量可用 外部类名.this.变量名,如:A.this.num
  • 在其他类中实例化内部类

    • //方法一
      A a = new A();
      B b = a.new B();//外部类对象.内部类类名();
      //方法二
      内部类 内部类变量名 = new 外部类().new 内部类();
      B b = new A().new B();//需要导包
      

2、静态内部类

  • public class A{
    	//定义内部类
    	static class B{
    		void show(){
                System.out.println("我是内部类");
            }
    	}
        void call(B b){
            b.show();
        }
        punlic stati void main(String[] args){
            A a = new A();
            a.call(new B());//相当于  B b = new B(); a.call(b);
        }
    }	
    
  • 静态内部类中可以有静态变量

  • 外部和内部类可以直接调用静态方法或者静态变量

  • 在其他类中实例化静态内部类

    静态内部类 静态内部类变量名 = new 静态内部类();
    A a = new A();//导包
    

二、匿名类

  • 没有明确的名称的类称为匿名类,匿名类也叫匿名内部类。

  • 匿名类属于局部内部类,局部内部类不能使用修饰符

  • 匿名类在编译后会产生多个.class文件:A.class、a$1.class

  • 例子

public class A{
	public static void main(String[] args){
        A a = new A(){};
        A b = new A(){};
    }
}
  • 注意:匿名类没有类名,所以没有构造方法
    • 匿名类可以访问外部类的变量
    • 匿名类可以访问方法中的变量
    • 匿名类必须实现接口/抽象类中所有抽象方法
    • 匿名类里不能定义静态的东西
    • 匿名类对普通类而言相当于继承,所有可以不重写普通类中的方法

心得体会

​ 晚上第一题就数兔子数晕了……今天就是晕晕的一天。


异常

#20190813

java中问题有两类:error(错误)和Exception(异常)

一、异常Exception

1、是指在程序运行过程中出现的非正常现象(比如:NullPointerException)

2、new Ex().getLength(str); 用匿名对象 new Ex() 调用方法 getLength(str)

3、当str ==null 时会报异常,可以加个判断 if (str == null )return 0;当你知道这句代码可能会出现异常时,要做处理异常的准备

处理异常

1、捕获异常
	try{
		//可能发生异常的代码
	}catch( /*会报出的异常类型*/ ){
		//捕获异常后需要执行的代码,也可以没有
	}
  • 例子:

    //使用try-catch捕获并处理异常
    		try {
    			int i=1,j=0,res;
    			System.out.println("begin");
    			res= i/j;
    			System.out.println("end");
    		}catch(Exception e) {
    			System.out.println("catched");
    			e.printStackTrace();
    		}
    		System.out.println("over");
    
    • 如果没有异常,catch(){ }不执行,其他照常执行
    • 如果try{}中发生了异常,异常后面的代码不执行,直接跳转到相应的catch(){ }中
    • 如果try{}中的异常和catch要捕获的异常不一致,则try{}中发生异常后的代码不执行
  • 多重catch处理异常

  • 例子:

    	Scanner sc = new Scanner(System.in);
    		try {
    			System.out.println("开始计算");
    			int i,j,res;
    			System.out.println("请输入被除数:");
    			i = sc.nextInt();
    			System.out.println("请输入除数:");
    			j = sc.nextInt();
    			res = i/j;
    			System.out.println( i + "/" + j + "=" + res);
    			System.out.println("计算结束");
    		}catch (InputMismatchException e) {
    			System.out.println("除数和被除数必须都是整数");
    		}catch (ArithmeticException e) {
    			System.out.println("除数不能为0");
    		}catch (Exception e) {
    			System.out.println("其他异常");
    		}finally {
    			System.out.println("感谢使用");
    		}
    		System.out.println("计算结束");
    

    输出结果:

    开始计算 开始计算 请输入被除数: 请输入被除数: 5 a 请输入除数: 除数和被除数必须都是整数 0 感谢使用 除数不能为0 计算结束 感谢使用 计算结束

    • 如果有多种异常,可以用多个catch分别处理不同的异常
    • catch异常的顺序是从子类到父类,最后一个一般是Exception
    • 因为如果把范围大的异常类放前面,后面范围小的就没机会执行了
    • 执行其中一条catch语句后,其后面的catch语句都会被忽略
2、处理异常
try{
		//可能发生异常的代码
	}catch( /*会报出的异常类型*/ ){
		//捕获异常后需要执行的代码,也可以没有
	}finally{ 
		//不论前面会不会发生异常,在这里的语句一定执行
	}
  • 在该结构中try{}是一定存在的,其他两个语句块为可选,至少选其一
  • 如果catch中存在return语句,会先执行finally再执行catch中的return;如果finally中也有return,就只return finally的值
  • finally唯一不执行的情况是:在异常处理代码中执行了 System.exit(1);直接退出java虚拟机
3、抛出异常
public static void divide() throws Exception{   //throws声明抛出了异常
    Scanner input = new Scanner(System.in);
    System.out.println("计算开始");
	int i,j,res;
	System.out.println("请输入被除数:");
	i = input.nextInt();
	System.out.println("请输入除数:");
	j = input.nextInt();
	res = i / j;
	System.out.println(i + "/" + j + "=" + res);
	System.out.println("计算结束");
}
  • 使用throws声明抛出异常,可以声明多个异常,用逗号隔开
  • Exception必须使用throws声明抛出异常
4、自定义异常
  • 自定义异常类一般包含两个构造方法:一个是无参的默认构造方法,另一个构造方法以字符串的形式接收一个定制的异常消息,并将该消息传递给超类的构造方法。

  • //自定义NameException
    public class NameException extends RuntimeException {
    	//自定义异常一般要继承自Exception或者RuntimeException
    public NameException(String mes) {
    		super("姓名输入不合法"+ mes);
    	}
    }
    
    • 定义异常类,并继承Exception或者RuntimeException

    • 编写异常类的构造方法,像=向父类构造方法传入异常描述信息,并继承父类的其他的实现方法

    • 实例化自定义异常对象,并在程序中使用throw抛出

    • throw与throws的区别

      • 作用不同:throw用于程序员自行产生并抛出异常,throws用于声明该方法内抛出异常
      • 使用位置不同:throw位于方法体内部,可以作为单独的语句使用;throws必须跟在方法参数列表的后面,不能单独使用
      • 内容不同:throw抛出一个异常对象,只能是一个;throws后面跟异常类,可以跟多个
    //异常
                //创建异常对象
                //IllegalNameException e = new IllegalNameException("用户名长度不能少6位");
                
                //手动抛出异常
                //throw e;
                throw new IllegalNameException("用户名长度不能少6位");
    

心得体会

​ 一本书都学完了,感觉时间过得好快,本子也用得很快;用了三年没用完的笔记本,在这里三个星期就用完了,真高兴。 :)