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的比较
| super | this |
|---|---|
| 调用父类的属性或方法 | 调用自身的属性或方法 |
| 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.class和A$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位"); -
心得体会
一本书都学完了,感觉时间过得好快,本子也用得很快;用了三年没用完的笔记本,在这里三个星期就用完了,真高兴。 :)