系列文章目录
文章目录
目录
前言
本笔记为B站尚硅谷Java入门视频教程(在线答疑+Java面试真题)的笔记,是笔者对于已经学过的大二Java课程的查缺补漏及巩固。部分C语言学过的基本语法将跳过。
一、学前经验
1.职业发展与提升
职场发展:
2.学习经验探讨
学习比重:
学习经验探讨:
●锻炼“双核”处理,边听讲思考,边做“笔记”
●纸上得来终觉浅,绝知此事要躬行!
>第一层次:看得懂(依赖于视频、书、贴子)
>第二层次:练的熟(每天代码必须实现2-3遍)
>总结:三分看,七分练
●建立行之有效的学习方法
>学习编程的捷径:敲,狂敲
>“模仿”好的编码习惯
>整理、回顾:每天花30min整理
二、Java语言概述
1.Java基础知识图解
2.Java语言运行机制及运行过程
Java虚拟机(JVM)
●JVM是一个虚拟的计算机,具有指令集并使用不同的存储区域。负责执行指令,管理数据、内存、寄存器。
●对于不同的平台,有不同的虚拟机。
●只有某平台提供了对应的java虚拟机,java程序才可在此平台运行。
●Java虚拟机机制屏蔽了底层运行平台的差别,实现了“一次编译,到处运行”。
垃圾回收
●不再使用的内存空间应回收一一垃圾回收。
>在CC++等语言中,由程序员负责回收无用内存。
>Jva语言消除了程序员回收无用内存空间的责任:它提供一种系统级线程跟踪存储空
间的分配情况。并在M空闲时,检查并释放那些可被释放的存储空间。
●垃圾回收在Java程序运行过程中自动进行,程序员无法精确控制和干预。
●Java程序还会出现内存泄漏和内存溢出问题吗?Yes!
什么是JDK,JRE
JDK(Java Development Kit Java开发工具包)
JDK是提供给Java开发人员使用的,其中包含了java的开发工具,也包括了JRE。所以安装了JDK,就不用在单独安装JRE了。
>其中的开发工具:编译工具(javac..exe)打包工具(jar.exe)等
JRE(Java Runtime Environment Java运行环境)
包括Java虚拟机JVM(Java Virtual Machine)和Java程序所需的核心类库等,如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。
简单而言,使用JDK的开发工具完成的java程序,交给JRE去运行。
JDK、JRE、JVM之间的关系
三、Java语言注释
1.单行注释
2.多行注释
3.文档注释(Java特有)
格式:
/**
@author指定java程序的作者
@version指定源文件的版本
*/
注释内容可以被JDK提供的工具javadoc所解析,生成一套以网页文件形式体现的该程序的说明文档。
操作方式:
四、关键字和保留字
1、关键字(keyword)的定义和特点
>定义:被Java语言赋予了特殊含义,用做专门用途的字符串(单词)
>特点:关键字中所有字母都为小写
2、保留字
●Java保留字:现有Java版木尚未使用,但以后版木可能会作为关键字使用。
自己命名标识符时要避免使用这些保留字
goto、const
五、标识符及命名规则
标识符:
>Java对各种变量、方法和类等要素命名时使用的字符序列称为标识符
>技巧:凡是自己可以起名字的地方都叫标识符。
定义合法标识符规则:
>由26个英文字母大小写,0-9,或$组成。 (数字不可以开头)
>不可以使用关键字和保留字,但能包含关键字和保留字。
>Java中严格区分大小写,长度无限制。
>标识符不能包含空格。
Java中的名称命名规范:
>包名:多单词组成时所有字母都小写:xxyyyzzz
>类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz
>变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz
>常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ
●注意1:在起名字时,为了提高阅读性,要尽量有意义,“见名知意”。
●注意2:java采用unicode字符集,因此标识符也可以使用汉字声明,但是不建议使用。
六、变量
●变量的概念:
>内存中的一个存储区域
>该区域的数据可以在同一类型范围内不断变化
>变量是程序中最基本的存储单元。包含变量类型、变量名和存储的值
●变量的作用:
>用于在内存中保存数据
●使用变量注意:
>Java中每个变量必须先声明,后使用
>使用变量名来访问这块区域的数据
>变量的作用域:其定义所在的一对{ }内
>变量只有在其作用域内才有效
>同一个作用域内,不能定义重名的变量
变量的分类——按数据类型:
需注意:字符串是一个类(class) 类型。
整数类型:byte、short、int、long
●Java各整数类型有固定的表数范围和字段长度,不受具体OS的影响,以保证java程序的可移植性。
●java的整型常量默认为int型,声明long型常量须后加'L'
long l1 = 35649819L;
●java程序中变量通常声明为int型,除非不足以表示较大的数,才使用long
浮点类型:float、double
●与整数类型类似,Java浮点类型也有固定的表数范围和字段长度,不受具体操作系统的影响。
●浮点型常量有两种表示形式:
>十进制数形式:如:5.12 512.0f .512(必须有小数点)
>科学计数法形式如:5.12e2 512E2 100E-2
●float:单精度,尾数可以精确到7位有效数字。很多情况下,精度很难满足需求。
double:双精度,精度是float的两倍。通常采用此类型。
●Java的浮点型常量默认为double型,声明float型常量,须后加‘f或‘F'。
float f1 = 12.5F;
七、面向对象(上)
1、面向对象的概念
面向过程(POP)与面向对象(OOP)
>二者都是一种思想,面向对象是相对于面向过程而言的。面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做。面向对象,将功能封装进对象,强调具备了功能的对象,以类对象为最小单位,考虑谁来做。
>面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如:抽象、分类、继承、聚合、多态等。
●面向对象的三大特征
>封装(Encapsulation)
>继承(Inheritance)
>多态(Polymorphism)
面向对象:Object Oriented Programming
面向过程:Procedure Oriented Programming
面向对象的思想概述
●类(Class)和对象(Object)是面向对象的核心概念。
>类是对一类事物的描述,是抽象的、概念上的定义
>对象是实际存在的该类事物的每个个体,因而也称为实例(instance):
●“万事万物皆对象”
●面向对象程序设计的重点是类的设计
●设计类,就是设计类的成员
->常见的类的成员:
1、属性:对应类中的成员变量
属性=成员变量=field=域、字段
2、行为:对应类中的成员方法
方法=成员方法=函数=method
●创建类的对象=类的实例化=实例化类
2、类和对象的使用
类和对象的使用过程:
1、创建类、类的成员
class Person{
String name;
int age = 1;
boolean isMale;
public void eat(){
System.out.println("人会吃饭。。");
}
public void sleep(){
System.out.println("人会睡觉。。");
}
public void talk(String language){
System.out.println("人会说话,说的是:"+language);
}
}
2、创建类的对象
Person p1 = new Person();
3、通过“对象.属性”或“对象.方法”调用对象的结构
p1.name = "zhangsan";
p1.isMale = true;
System.out.println(p1.name);
p1.sleep();
p1.eat();
p1.talk("Chinese");
整体代码实现:
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "zhangsan";
p1.isMale = true;
System.out.println(p1.name);
p1.sleep();
p1.eat();
p1.talk("Chinese");
}
}
class Person{
String name;
int age = 1 ;
boolean isMale;
public void eat(){
System.out.println("人会吃饭。。");
}
public void sleep(){
System.out.println("人会睡觉。。");
}
public void talk(String language){
System.out.println("人会说话,说的是:"+language);
}
}
注:如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性(非static的)
3、对象的内存解析
●堆(heap):此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在Java虚拟机规范的描述是:所有的对象实例以及数组都要在堆上分配。
●栈(stack):通常所说的栈,是指虚拟机栈。虚拟机栈用于存储局部变量等。局部变量存放了编译期可知长度的各种基本数据类型、对象引用。方法执行完,自动释放。
●方法区(Method Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
4、属性与局部变量的对比
●相同点:
1.定义变量的格式:数据类型 变量名 = 变量值
2.先声明,后使用
3.变量都有其对应的作用域
●不同点:
1.在类中声明的位置不同
属性:直接定义在类的一对{}内
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
2.关于权限修饰符的不同
属性:可以在声明属性时,指明其权限,使用权限修饰符
常用的修饰符:private、public 、缺省、protected
局部变量:不可使用权限修饰符
3.默认初始化值的情况不同
属性:类的属性,根据其类型,都有默认初始化值。
局部变量:没有初始化值。(意味着我们在调用局部变量之前,一定要显示赋值。(特别地:形参在调用时,我们赋值即可))
4.在内存中加载的位置不同
属性:加载到堆空间中 (非static)
局部变量:加载到栈空间
5、方法的举例与声明的格式
●方法的分类:按照是否有形参及返回值
●方法的声明:
权限修饰符 返回值类型 方法名(形参列表){
方法体
}
注:方法的使用中,可以调用当前类的属性或方法。
练习:
public class School {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.name = "张三";
teacher.age = 50;
teacher.teachAge = 30;
teacher.course = "数学";
teacher.say();
Student student = new Student();
student.name = "李四";
student.age = 20;
student.major = "软件工程";
student.interests = "吃饭,睡觉,打豆豆";
student.say();
}
}
class Teacher{
String name;
int age;
int teachAge;
String course;
public void say(){
System.out.println("教师姓名:" +name+ "\n年龄:" +age+ "\n教龄:" +teachAge+ "\n教授课程:" +course);
}
}
class Student{
String name;
int age;
String major;
String interests;
public void say(){
System.out.println("学生姓名:" +name+ "\n年龄:" +age+ "\n专业:" +major+ "\n兴趣爱好:" +interests);
}
}
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "李狗蛋";
p1.age = 18;
p1.sex = 1;
p1.study();
p1.showAge();
int newAge = p1.addAge(2);
System.out.println(p1.name + "的新年龄为:" +newAge+ "岁");
p1.showSex();
p1.age = p1.addAge(5);
p1.showAge();
p1.age = p1.age - 10;
p1.showAge();
System.out.println("_____________________");
Person p2 = new Person();
p2.showAge();
p2.addAge(30);
p2.showAge();
p1.showAge();
}
}
public class Person {
String name;
int age;
int sex;
public void study() {
System.out.println("studing...");
}
public void showAge() {
System.out.println("年龄为:" + age);
}
public int addAge(int i) {
age = age + i;
return age;
}
public void showSex() {
if (sex == 0) {
System.out.println("性别为女");
} else {
System.out.println("性别为男");
}
}
}
6、方法的重载(overload)
重载的概念
在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或参数类型不同即可。
两同一不同:
相同:同一个类、相同方法名
不同:参数列表不同(参数个数不同、参数类型不同)
判断是否重载:跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系!
可变个数形参
JavaSE5.0中提供了Varargs(variable number of arguments)机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。
JDK5.0以前,采用数组形参来定义方法,传入多个同一类型变量
public static void test(int a,String[] books);
JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量
使用方式:在最后一个形参类型后增加三点(...),则表示该形参可以接受多个参数值,多个参数值是被当成数组传入
public static void test(int a,String...books);
重载中使用可变参数:
/**
* 可变参数
*/
public void test(String a,int... b){
for (int i : b) {
Log.i("test:","b:" + i);
}
}
public void test(String a,int b){
}
值传递机制
变量的赋值:
*如果变量是基本数据类型,此时赋值的是变量所保存的数据值。
*如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值。
例子:
public class ValueTransferTest {
public static void main(String[] args) {
int m = 1;
int n = m;
System.out.println("----------基础数据类型-------");
System.out.println("m = " + m + " ,n = " + n);
n = 500;
System.out.println("m = " + m + " ,n = " + n);
System.out.println("----------基础数据类型-------\n");
System.out.println("----------引用数据类型-------");
Order o1 = new Order();
o1.orderId = 100;
Order o2 = o1;
System.out.println("o1 = " +o1.orderId+ " , o2 = " + o2.orderId);
o2.orderId = 333;
System.out.println("o1 = " +o1.orderId+ " , o2 = " + o2.orderId);
System.out.println("----------引用数据类型-------\n");
}
}
class Order{
int orderId;
}
结果:
*方法形参的传递机制:值传递
形参:方法定义时,声明的小括号内的参数
实参:方法调用时,实际传递给形参的数据
值传递机制:
1.如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值。
例:
public class ValueTransferTest1 {
public static void main(String[] args) {
int m = 10;
int n = 20;
System.out.println("m = " + m + " ,n = " + n);
ValueTransferTest1 test = new ValueTransferTest1();
test.swap(m,n);
System.out.println("m = " + m + " ,n = " + n);
}
public void swap(int m,int n){
int temp = m;
m = n ;
n = temp;
}
}
结果:
可以看到,m,n并没有交换数据值。原因:
变量存储在栈中,而方法运行完后,会自动释放。
2.如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值。
例:
public class ValueTransferTest2 {
public static void main(String[] args) {
Data data = new Data();
data.m = 10;
data.n = 20;
System.out.println("m = " + data.m + " ,n = " + data.n);
ValueTransferTest2 test2 = new ValueTransferTest2();
test2.swap(data);
System.out.println("m = " + data.m + " ,n = " + data.n);
}
public void swap(Data data){
int temp = data.m;
data.m = data.n;
data.n = temp;
}
}
class Data{
int m;
int n;
}
结果:
7、封装性的引入
封装和隐藏
为什么要封装?
我们程序设计追求“高内聚,低耦合”。
高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
低耦合:仅对外暴露少量的方法用于使用。
封装的定义:
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来 。这就是封装性的设计思想。
public class AnimalTest {
public static void main(String[] args) {
Animal animal = new Animal();
animal.name = "花花";
animal.age = 6;
animal.setLegs(4);
animal.show();
}
}
class Animal{
String name;
int age;
private int legs;
public void setLegs(int x){
if(x >=0 && x % 2 == 0){
legs = x;
}else{
legs = 0;
}
}
public int getLegs() {
return legs;
}
public void show(){
System.out.println(" name:" + name + " , age: " + age + ", legs: " + legs);
}
}
在实际问题中,我们往往需要给属性赋值加入额外的限制条件。这个条件不能在属性声明时体现,我们只能通过方法进行限制条件的添加。(比如setlegs())。同时,我们需要避免用户再使用“对象.属性”的方式对属性赋值。则需要将属性声明为私有的(private)
●封装性的体现
我们将类的属性xxx私有化(private),同时 ,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
8、四种权限修饰
Java规定的4种权限(从小到大排列):private、缺省(default)、protected、public
4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
修饰类的话:只能使用缺省(default)、public
9、构造器
构造器的作用:创建对象 , 初始化属性
*如果没有显式的定义类的构造器的话,则系统默认提供一个空参的构造器
(如果我们显式的定义了类的构造器之后,系统不再提供默认的空参构造器)
*定义构造器的格式: 权限修饰符 类名(形参列表){ }
*一个类中定义的多个构造器,彼此构成重载
创建类的对象: new + 构造器 ,如:
Person p = new Person();
可构建多个构造器,如
public Person(){
System.out.println("Person()....");
}
public Person(String n){
name = n;
}
public Person(String n, int a){
name = n;
age = a;
}
10、属性赋值的先后顺序及UML类图
顺序:
①默认初始化
②显式初始化
③构造器中赋值
④通过“对象.方法”或“对象.属性”的方式赋值
UML类图:
11、关键字:this 的引入
this关键字的使用:
this可以用来修饰:属性、方法、构造器
①this修饰属性和方法: this理解为:当前对象 或当前正在创建的对象
在类的方法中,我们可以使用“this.属性”或“this.方法”的方式,调用当前对象属性或方法。但是 通常情况下,我们都选择省略“this.”;特殊情况下,如果方法的形参和类的属性同名是,我们必须显式的使用“this.变量”的方式,表明此变量是属性,而非形参。
public class PersonTest {
public static void main(String[] args) {
Person person = new Person();
person.setAge(1);
System.out.println(person.getAge());
person.work();
}
}
class Person{
private String name;
private int age;
public void work(){
System.out.println("working......");
this.eat();
System.out.println("working.........");
}
public void eat(){
System.out.println("eating.....");
}
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;
}
}
②this修饰构造器:
*我们在类的构造器中,可以显式的使用“this(形参列表)”方式,调用本类中指定的其他构造器
*构造器中不能通过“this(形参列表)”方式调用自己
*如果一个类中有n个构造器,最多有n-1个构造器中使用了“this(形参列表)”
*规定:“this(形参列表)”必须声明在当前构造器的首行
*构造器内部,最多只能声明一个“this(形参列表)”用来调用其他构造器
public class PersonTest {
public static void main(String[] args) {
Person person = new Person();
person.setAge(1);
System.out.println(person.getAge());
person.work();
System.out.println();
Person person1 = new Person("wang",5);
}
}
class Person{
private String name;
private int age;
public void work(){
System.out.println("working......");
this.eat();
System.out.println("working.........");
}
public void eat(){
System.out.println("eating.....");
}
public Person(){
this.eat();
}
public Person(String name){
this();
this.name = name;
this.eat();
}
public Person(int age){
this();
this.age = age;
this.eat();
}
public Person(String name, int age){
this(age);
this.name = name;
//this.age = age;
this.eat();
}
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;
}
}
八、面向对象(中)
1、继承性
*继承性的好处:
①减少了代码的冗余,提高了代码的复用性
②便于功能的扩展
③为之后多态性的使用,提供了前提
*继承性的格式:class A extends B{}
A: 子类、派生类、subclass
B:父类、超类、基类、superclass
体现:一旦子类A继承父类B以后,子类A就获取了B中声明的结构:属性、方法
(特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中 私有的结构,只是因为封装性的影响,使得子类不能直接调用父类的结构而已。)
子类继承父类后,还可以声明自己特有的属性或方法:实现功能的拓展、
*Java中关于继承性的规定:
①一个类可以被多个子类继承
②Java中类的单继承性:一个类只能有一个父类
③子父类是相对的概念
④子类直接继承的父类,叫做直接父类,间接继承的父类叫间接父类
⑤子类继承父类以后,就获取了直接继承父类以及所有间接继承父类中声明的属性和方法
(注意:所有的Java类都直接或间接的继承于java.lang.Object类)
练习:
public class KidsTest {
public static void main(String[] args) {
Kids someKid = new Kids(10);
someKid.printAge();
someKid.setSalary(0);
someKid.setSex(1);
someKid.manOrWoman();
someKid.employeed();
}
}
public class Mankind {
int sex;
int salary;
public Mankind() {
}
public Mankind(int sex, int salary) {
this.sex = sex;
this.salary = salary;
}
public void manOrWoman(){
if(sex == 1){
System.out.println("this is a man");
}
else{
System.out.println("this is a woman");
}
}
public void employeed(){
if(salary == 0){
System.out.println("no job");
}
else {
System.out.println("job");
}
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
}
public class Kids extends Mankind{
int yearsOld;
public Kids(int yearsOld) {
this.yearsOld = yearsOld;
}
public void printAge(){
System.out.println("I am "+yearsOld+" years old");
}
public int getYearsOld() {
return yearsOld;
}
public void setYearsOld(int yearsOld) {
this.yearsOld = yearsOld;
}
}
2、方法的重写
定义:在子类中可以根据需要对从父类中继承的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
重写的规定:
①子类重写的方法名和形参列表与父类被重写的方法名和形参列表相同
②子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
(特殊情况:子类不能重写父类中声明为private权限的方法)
③返回值类型:
^ 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
^父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型只能是A类或A的子类。
^父类被重写的方法的返回值类型是基本数据类型(如:double),则子类重写的方法的返回值类型只能是相同的基本数据类型(double)
练习:
public class KidsTest {
public static void main(String[] args) {
Kids someKid = new Kids(10);
someKid.printAge();
someKid.setSalary(0);
someKid.setSex(1);
someKid.manOrWoman();
someKid.employeed();
}
}
public class Mankind {
int sex;
int salary;
public Mankind() {
}
public Mankind(int sex, int salary) {
this.sex = sex;
this.salary = salary;
}
public void manOrWoman(){
if(sex == 1){
System.out.println("this is a man");
}
else{
System.out.println("this is a woman");
}
}
public void employeed(){
if(salary == 0){
System.out.println("no job");
}
else {
System.out.println("job");
}
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
}
public class Kids extends Mankind{
int yearsOld;
public Kids(int yearsOld) {
this.yearsOld = yearsOld;
}
public void printAge(){
System.out.println("I am "+yearsOld+" years old");
}
@Override
public void employeed() {
System.out.println("Kids should study and no job ");
}
public int getYearsOld() {
return yearsOld;
}
public void setYearsOld(int yearsOld) {
this.yearsOld = yearsOld;
}
}
3、关键字super
super关键字的使用
1.super理解为:父类的
2.super可以用来调用:属性、方法、构造器
3.super的使用
3.1 我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
3.2 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性。
3.3 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的 方法时则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
4.super调用构造器
4.1我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
4.2 "super(形参列表)"的使用,必须声明在子类构造器的首行!
4.3我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现
4.4在构造器的首行,没有显式的声明"this(形参列表)"或"super(形参列表)",则默认调用的是父类中空参的构造器:super()
4.5在类的多个构造器中,至少有一个类的构造器中使用了"super(形参列表)",调用父类中的构造器
5.子类对象实例化的全过程
①从结果上来看:(继承性)
子类继承父类以后,就获取了父类中声明的属性或方法。
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
②从过程上来看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,...直到调用了java.lang.object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用。
明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
4、多态性
1.理解多态性:
可以理解为一个事物的多种形态。
2.何为多态性:
对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用〕
例:Person p1 = new Man();
Animal a1 = new Cat();
3.多态的使用: 虚拟方法调用
有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
(所以不能用子类独有的方法,如 p2.earnmoney() ,会发生报错,说此方法没有在Person中声明)
总结:编译看左边,运行看右边
4.多态性的使用前提:
①类的继承关系
②方法的重写
多态的使用举例:
public class AnimalTest {
public static void main(String[] args) {
AnimalTest test = new AnimalTest();
test.func(new Dog());
}
public void func(Animal animal){ //实际:Animal animal = new Dog();
animal.eat();
animal.roar();
}
/*
如果没有多态,要写多个方法:
public void func(Dog dog){
dog.eat();
dog.roar();
}
public void func(Cat cat){
cat.eat();
cat.roar();
}
*/
}
class Animal{
public void eat(){
System.out.println("动物:吃");
}
public void roar(){
System.out.println("动物:吼叫");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("狗吃肉");
}
public void roar(){
System.out.println("汪汪汪");
}
}
class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
public void roar(){
System.out.println("喵喵喵");
}
}
5.对象的多态性,只适用于方法,不适用于属性(编译运行都看左边)
6.向下转型的使用:
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。
如何才能调用子类特有的属性和方法?
向下转型:使用强制类型转换符。
Man m1 = (Man) p2
m1.earnmoney();
7.instanceof关键字的使用:
a instanceof A : 判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
注:
如果类B是类A的父类,a instanceof A返回true,则 a instanceof B也返回true
多态练习一:
public class FieldMethodTest {
public static void main(String[] args) {
Sub s = new Sub();
System.out.println(s.count);
s.display();
Base b = s; //多态性
// ==:对于引用数据类型来讲,比较的是两个引用数据类型变量的地址值是否相同
System.out.println(b==s);
System.out.println(b.count);
b.display();
}
}
class Base{
int count = 10;
public void display(){
System.out.println(this.count);
}
}
class Sub extends Base{
int count = 20;
public void display(){
System.out.println(this.count);
}
}
总结:
1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。
2.对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中的实际变量
多态练习二:
public class InstanceTest {
public static void main(String[] args) {
InstanceTest test = new InstanceTest();
test.method(new Student());
}
public void method(Person e){
String info =e.getInfo();
System.out.println(info);
if(e instanceof Graduate){
System.out.println("a graduated student\na student\na person");
}else if(e instanceof Student){
System.out.println("a student\na person");
}else {
System.out.println("a person");
}
}
}
class Person{
protected String name = "person";
protected int age = 50;
public String getInfo(){
return "Name : " + name + "\n" + "Age : " + age;
}
}
class Student extends Person{
protected String school = "pku";
public String getInfo(){
return "Name : " + name + "\n" + "Age : " + age + "\nSchool : " + school;
}
}
class Graduate extends Student{
public String major = "IT";
public String getInfo(){
return "Name : " + name + "\n" + "Age : " + age + "\nSchool : " + school + "\nMajor : " + major;
}
}
5、==与equals()
1、== 运算符:
①.可以使用在基本数据类型变量和引用数据类型变量中
②.如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
如:
int i = 10;
double d = 10.0;
System.out.println(i==d);
如果比较的是引用数据类型变量:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
如:
Customer cu1 = new Customer("Tom",21);
Customer cu2 = new Customer("Tom",21);
System.out.println(cu1 == cu2);
2、equals()方法的使用:
①.是一个方法,而非运算符
②.只能适用于引用数据类型
③. object类中equals()的定义:
public boolean equals (object obi) {
return (this ==obi);
}
说明: Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同
即两个引用是否指向同一个对象实体
④.像String、Date、File、包装类等都重写了0bject类中的equals()方法。重写以后
比较的不是两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同。
如:
String s1 = new String("hhh");
String s2 = new String("hhh");
System.out.println(s1==s2);System.out.println("-------------------------");
System.out.println(cu1.equals(cu2));
System.out.println(s1.equals(s2));
⑤、通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们就需要对Object类中的equals()进行重写
手动生成:
@Override
public boolean equals(Object obj) {
if(this == obj){
return true;
}
if(obj instanceof Customer){
Customer cust = (Customer)obj;
return this.age == cust.age && this.name.equals(cust.name);
}
else {
return false;
}
}
自动生成:
两者的区别:
重写equals()方法的原则:
示例:
public class EqualsTest {
public static void main(String[] args) {
int i = 10;
int j = 10;
double d = 10.0;
System.out.println(i==j);
System.out.println(i==d);
boolean b = true;
//System.out.println(i == b); 报错Error:(12, 30) java: 不可比较的类型: int和boolean
char c = 10;
System.out.println(i==c);
char c1 = 'A';
char c2 = 65;
System.out.println(c1==c2);
Customer cu1 = new Customer("Tom",21);
Customer cu2 = new Customer("Tom",21);
System.out.println(cu1 == cu2);
String s1 = new String("hhh");
String s2 = new String("hhh");
System.out.println(s1==s2);
System.out.println("-------------------------");
System.out.println(cu1.equals(cu2));
System.out.println(s1.equals(s2));
}
}
public class Customer {
String name;
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 Customer(String name, int age) {
this.name = name;
this.age = age;
}
public Customer() {
}
}
运行结果:
6、object类中toString()的使用
1.当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
2. object类中toString()的定义:
pubiic string toString(){
return getclass().getName() +“@”+ Integer.toHexString(hashCode());
}
3.像String. Date.File、包装类等都重写了Object类中的toString()方法。
使得在调用对象的tostring()时,返回“实体内容"信息.
4.自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容"
自动生成ToString方法:
7、Java中的JUnit单元测试
步骤:
1.导包
2.创建Java类,进行单元测试。
此时的Java类要求:此类是public的 ②此类提供公共的无参的构造器
3.此类中声明单元测试方法。
此时的单元测试方法:方法的权限是public,没有返回值,没有形参
4.此单元测试方法上需要声明注解:@Test,并在单元测试类中导入: import org.junit.Test;
8、包装类(Wrapper)的使用
1.java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
2.掌握的:基本数据类型、包装类、String三者之间的相互转换
①基本数据类型--->包装类:调用包装类的构造器
package Wrapper;
import org.junit.Test;
public class WrapperTest {
@Test
public void test1(){
int num1 = 10;
Integer in1 = new Integer(num1);
System.out.println(in1.toString());
Integer in2 = new Integer("123");
System.out.println(in2.toString());
Boolean bo1 = new Boolean(true);
Boolean bo2 = new Boolean("true");
Boolean bo3 = new Boolean("TrUe");
Boolean bo4 = new Boolean("TrUe1");
System.out.println(bo1.toString());
System.out.println(bo2.toString());
System.out.println(bo3.toString());
System.out.println(bo4.toString());
Order order = new Order();
System.out.println(order.isMale);
System.out.println(order.isFemale);
}
}
class Order{
boolean isMale;
Boolean isFemale;
}
结果:
Boolean中判断String规则:
public static boolean parseBoolean(String s) { return ((s != null) && s.equalsIgnoreCase("true")); }
不区分大小写,如果一样就是true,其他都是false
注:
Order order = new Order(); System.out.println(order.isMale); System.out.println(order.isFemale); } } class Order{ boolean isMale; Boolean isFemale; }
中,System.out.println(order.isFemale)显示为null
②包装类---->基本数据类型:调用包装类的xxxValue()
@Test
public void test2(){
Integer in1 = new Integer(12);
int i1 = in1.intValue();
System.out.println(i1);
}
③自动装箱与自动拆箱
@Test
public void test3(){
int num1 = 10;
method(num1); //自动装箱
int num2 = 15;
Integer in1 = num2; //自动装箱
boolean b1 = true;
Boolean b2 = b1; //自动装箱
int num3 = in1; //自动拆箱
}
public void method(Object obj){
System.out.println(obj);
}
④基本数据类型、包装类--->String型:调用String重载的valueOf(Xxxxxx)
@Test
public void test4(){
int num1 = 10;
//方法一:连接运算
String str = num1 + "";
System.out.println(str);
//方法二:String.valueOf(Xxx xxx)
float f1 = 12.3f;
String str1 = String.valueOf(f1);
System.out.println(str1);
}
⑤String类型–-->基本数据类型、包装类:调用包装类的parseXxx(String s)
@Test
public void test5(){
//调用包装类的parseXxx()
String str = "123";
int num1 = Integer.parseInt(str);
System.out.println(num1);
}
面试题:
@Test
public void Test6(){
Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1);
}
结果:
1.0
原因:
1、三目运算符只能使用基本数据类型,所以编译自动拆箱为int、double
2、三目运算符要求数据类型一致,所以编译int自动提升为double
@Test
public void Test7(){
Object o2;
if(true)
o2 = new Integer(1);
else
o2 = new Double(2.0);
System.out.println(o2);
}
结果:1
原因:没有类型提升
结果:
false
true
false
原因:
Integer内部定义了IntegerCatch结构,Integercatch中定义了数组Integer[],保存了从-128—127范围的整数,如果我们使用自动装箱的方式,给Integer赋值的范围在-128—127范围内时,可以直接使用数组中的元素,不用再去new。
而128则超出了范围,所以会new;
九、面向对象(下)
1、关键字:static
①.static:静态的
②.static可以用来修饰:属性、方法、代码块、内部类
③.使用static修饰属性:静态变量
属性,按是否使用static修饰,又分为:静态属性(类变量)VS 非静态属性(实例变量)
实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改 其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。
静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。
public class StaticTest {
public static void main(String[] args) {
Chinese c1 = new Chinese();
c1.name = "姚明";
c1.age = 40;
c1.nation = "China";
Chinese c2 = new Chinese();
c2.name = "马龙";
c2.age = 28;
System.out.println(c2.nation);
Chinese c3 = new Chinese();
c3.name = "刘翔";
c3.age = 35;
c3.nation = "CN";
System.out.println(c1.nation);
}
}
class Chinese{
String name;
int age;
static String nation;
}
static修饰属性的其他说明:
静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用
静态变量的加载要早于对象的创建。
由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。
静态属性举例: System.out、 Math.PI
类变量和实例变量的内存解析:
④.使用static修饰方法:静态方法
随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
静态方法中,只能调用静态的方法或属性
非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
⑤. static注意点:
在静态的方法内,不能使用this关键字、super关键字
关于静态属性和静态方法的使用,大家都从生命周期的角度去理解。
⑥.开发中,如何确定一个属性是否要声明为static的?
>属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
>操作静态属性的方法,通常设置为static的
>工具类中的方法,习惯上声明为static的。比如:Math、Arrays. collections
2、单例设计模式
设计模式:
设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模免去我们自己再思考和摸索。式就像是经典的棋谱,不同的棋局,我们用不同的棋谱。 ("套路”)
单例设计模式
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
单例的饿汉式实现:
public class SingletonTest1 {
public static void main(String[] args) {
Bank bank1 = Bank.getInstance();
Bank bank2 = Bank.getInstance();
System.out.println(bank1 == bank2);
}
}
//饿汉式
class Bank{
//1.私有化构造器
private Bank(){
}
//2.内部创建类的对象
//4.要求此对象也必须声明为静态的
private static Bank instance = new Bank();
//3.提供公共的静态的方法,返回类的对象
public static Bank getInstance(){
return instance;
}
}
单例的懒汉式实现:
package SingletonTest;
public class SingletonTest2 {
public static void main(String[] args) {
Order o1 = Order.getInstance();
Order o2 = Order.getInstance();
System.out.println(o1 == o2);
}
}
//懒汉式
class Order{
//1.私有化构造器
private Order(){
}
//2.声明当前类对象,没有初始化
//4.此对象也必须声明为static的
public static Order instance = null;
//3.声明public、static的返回当前类对象的方法
public static Order getInstance() {
if (instance == null) {
instance = new Order();
}
return instance;
}
}
区分饿汉式和懒汉式:
饿汉式:
坏处: 对象加载时间过长。
好处: 饿汉式是线程安全的
懒汉式:
好处: 延迟对象的创建。
目前的写法坏处: 线程不安全。 ——>到多线程内容时,再修改
单例设计模式应用场景:
3、main()方法的使用说明
①. main()方法作为程序的入口
②. main()方法也是一个普通的静态方法
public class MainTest {
public static void main(String[] args) {
Main.main(new String[100]);
}
}
class Main{
public static void main(String[] args) {
for (int i = 0;i<args.length;i++){
args[i] = "args_" + i;
System.out.println(args[i]);
}
}
}
③. main()方法可以作为我们与控制台交互的方式。(之前:使用Scanner)
public class MainDemo {
public static void main(String[] args) {
for(int i=0;i<args.length;i++)
System.out.println("***" + args[i]);
}
}
4、代码块
①.代码块的作用:用来初始化类、对象
②.代码块如果有修饰的话,只能使用static.
③.分类:静态代码块 vs 非静态代码块
④.静态代码块
>内部可以有输出语句
>随着类的加载而执行,而且只执行一次
>作用:初始化类的信息
>如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
>静态代码块的执行要优先于非静态代码块的执行
>静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
⑤.非静态代码块
>内部可以有输出语句
>随着对象的创建而执行,每创建一次对象,就执行一次非静态代码块
>作用:可以在创建对象时,对对象的属性等进行初始化
>如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
>非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
对属性可以赋值的位置:
1.默认初始化
2.显式初始化
3.构造器中初始化
4.有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值
5.在代码块中赋值
(属性赋值执行的先后顺序:1---2/5---3---4)
5、关键字:final
final: 最终的
①. final可以用来修饰的结构: 类、方法、变量
②.final用来修饰一个类: 此类不能被其他类所继承。
比如:String类,System类,StringBuffer类
③.final用来修饰方法: 表明此方法不可以被重写
比如:object类中getclass();
④.final用来修饰变量: 此时的"变量"就称为是一个常量
final修饰属性:可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化
final修饰局部变量:
尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋 一个实参。一旦以后,就只能在方法体内使用此形参,但不能进行重新赋值。
static final 用来修饰属性: 全局常量
6、抽象类与抽象方法
①.abstract:抽象的
②.abstract可以用来修饰的结构:类、方法
③. abstract修饰类:抽象类
>此类不能实例化
>抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
>开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作
④.abstract修饰方法:抽象方法
>抽象方法只有方法的声明,没有方法体
>包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法。
>若子类重写了父类中的所有的抽象方法后,此子类方可实例化
若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
abstract使用上的注意点:
1.abstract不能用来修饰:属性、构造器等结构
2.abstract不能用来修饰私有方法、静态方法、final的方法、final的类
练习:
public class PayrollSystem {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
int month = calendar.get(Calendar.MONTH)+1; //获取当前的月份 (一月份为0,以此推类)
System.out.println("当前月份:" + month);
Employee[] emps = new Employee[2];
emps[0] = new SalariedEmployee("刘德华",1002,new MyDate(1992,11,26),10000);
emps[1] = new HourlyEmployee("张学友",1003,new MyDate(1991,3,20),60,240);
for(int i = 0;i<emps.length;i++){
System.out.println(emps[i]);
double salary = emps[i].earnings();
System.out.println("月工资为:" + salary);
if(month == emps[i].getBirthday().getMonth()){
System.out.println("生日快乐!奖励100元");
System.out.println();
}
}
}
}
public class SalariedEmployee extends Employee {
private double monthlySalary;
public SalariedEmployee(String name, int number, MyDate birthday) {
super(name, number, birthday);
}
public SalariedEmployee(String name, int number, MyDate birthday, double monthlySalary) {
super(name, number, birthday);
this.monthlySalary = monthlySalary;
}
public double getMonthluSalary() {
return monthlySalary;
}
public void setMonthluSalary(double monthlySalary) {
this.monthlySalary = monthlySalary;
}
@Override
public double earnings() {
return monthlySalary;
}
public String toString(){
return "SalariedEmployee[" + super.toString() + "]";
}
}
public class HourlyEmployee extends Employee {
private int wage;
private int hour;
public HourlyEmployee(String name, int number, MyDate birthday) {
super(name, number, birthday);
}
public HourlyEmployee(String name, int number, MyDate birthday, int wage, int hour) {
super(name, number, birthday);
this.wage = wage;
this.hour = hour;
}
public int getWage() {
return wage;
}
public void setWage(int wage) {
this.wage = wage;
}
public int getHour() {
return hour;
}
public void setHour(int hour) {
this.hour = hour;
}
@Override
public double earnings() {
return wage*hour;
}
public String toString(){
return "HourlyEmployee[" + super.toString() + "]";
}
}
public abstract class Employee {
private String name;
private int number;
private MyDate birthday;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
public Employee(String name, int number, MyDate birthday) {
this.name = name;
this.number = number;
this.birthday = birthday;
}
public abstract double earnings();
@Override
public String toString() {
return
"name='" + name + '\'' +
", number=" + number +
", birthday=" + birthday.toDateString() ;
}
}
public class MyDate {
private int year;
private int month;
private int day;
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public String toDateString(){
return year + "年" + month + "月" + day + "日";
}
}
7、接口 Interface
接口的使用
1.接口使用interface来定义
2.Java中,接口和类是并列的两个结构
3.如何定义接口:定义接口中的成员
3.1 JDK7及以前:只能定义全局常量和抽象方法
>全局常量:public static final的 (但是书写时,可以省略不写)
>抽象方法: public abstract的 (但是书写时,可以省略不写)
3.2JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
4.接口中不能调用构造器。(意味着接口不能实例化)
5.Java开发中,接口通过让类去实现(implements)的方式来使用.
如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
6.Java类可以实现多个接口--->弥补了Java单继承性的局限性
格式: class AA extends BB implements CC, DD,EE
7.接口与接口之间可以继承,而且可以多继承
8.接口的具体使用,体现多态性
9.接口,实际上可以看做是一种规范
匿名实现类:
1.创建了接口的非匿名实现类的非匿名对象
Computer com = new Computer();
Flash flash = new Flash();com.transferData(flash);
2.创建了接口的非匿名实现类的匿名对象
com.transferData(new Printer());
3.创建了接口的匿名实现类的非匿名对象
USB phone = new USB(){
@Override
public void start() {
System.out.println("手机开始工作");
}
@Override
public void stop() {
System.out.println( "手机结束工作");
}
};
com.transferdata(phone);
4.创建了接口的匿名实现类的匿名对象
com.transferData(new USB(){
@Override
public void start() {
System.out.println( "mp3开始工作");
}
@Override
public void stop() {
System.out.println( "mp3结束工作");
}
});
Java8中接口的新特性
知识点1:接口中定义的静态方法,只能通过接口来调用。
知识点2:通过实现类的对象,可以调用接口中的默认方法,
如果实现类重写了接口中的默认方法,调用时,调用的是重写的方法
知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,
那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。(类优先原则
知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
那么在实现类没有重写此方法的情况下,报错。(接口冲突
这就需要我们必须在实现类中重写此方法
代码如下:
public interface CompareA {
//静态方法
public static void method1(){
System.out.println("CompareA:北京");
}
//默认方法
public default void method2(){
System.out.println("CompareA:上海");
}
default void method3(){
System.out.println("CompareA:上海");
}
}
public class SubClassTest {
public static void main(String[] args) {
SubClass s = new SubClass();
// s.method1();
//知识点1:接口中定义的静态方法,只能通过接口来调用。
CompareA.method1();
//知识点2:通过实现类的对象,可以调用接口中的默认方法
//如果实现类重写了接口中的默认方法,调用时,调用的是重写的方法
s.method2();
//知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,
//那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。(类优先原则
//知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
// 那么在实现类没有重写此方法的情况下,报错。(接口冲突
//这就需要我们必须在实现类中重写此方法
s.method3();
}
}
class SubClass extends SuperClass implements CompareA{
@Override
public void method2() {
System.out.println("SubClass:上海");
}
}
public class SuperClass {
public void method3(){
System.out.println("SuperClass:北京");
}
}
8、内部类
何时需要使用内部类:
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
内部类定义:
在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。
内部类的分类:
成员内部类(静态、非静态)
局部内部类(方法内、代码块内、构造器内):
一方面,作为外部类的成员:
->调用外部类的结构
->可以被static修饰
->可以被4种不同的权限修饰
另一方面,作为一个类:
->类内可以定义属性、方法、构造器
->可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承可以被继承
->可以被abstract修饰
如何实例化成员内部类的对象:
//创建dog实例(静态的成员内部类
Person.Dog dog = new Person.Dog();
dog.show();
//创建Bird实例(非静态的成员内部类
Person p = new Person();
Person.Bird bird = p.new Bird();
bird.sing();
如何在成员内部类中区分调用外部类的结构:
public void display(String name){
System.out.println(name);//方法的形参
System.out.println(this.name);//内部类的属性
System.out.println(Person.this.name);//外部类的属性
}
开发中局部内部类的使用:
public class InnerClassTest1 {
//开发中很少见
public void method(){
//局部内部类
class AA{
}
}
//返回一个实现了Comparable接口的类的对象
public Comparable getComparable(){
//创建一个实现了Comparable接口的类:局部内部类
class MyComparable implements Comparable{
@Override
public int compareTo(Object o) {
return 0;
}
}
return new MyComparable();
}
}
整体代码实现:
public class InnerClassTest {
public static void main(String[] args) {
//创建dog实例(静态的成员内部类
Person.Dog dog = new Person.Dog();
dog.show();
//创建Bird实例(非静态的成员内部类
Person p = new Person();
Person.Bird bird = p.new Bird();
bird.sing();
bird.display("黄鹂");
}
}
class Person{
String name = "小明";
int age;
public void eat(){
System.out.println("人:吃饭");
}
//静态成员内部类
static class Dog{
String name;
int age;
public void show(){
System.out.println("汪汪汪");
}
}
//非静态成员内部类
class Bird{
String name = "杜鹃";
public Bird(){
}
public void sing(){
System.out.println("唧唧咋咋");
Person.this.eat();//调用外部类的非静态属性 (可直接 eat()
}
public void display(String name){
System.out.println(name);//方法的形参
System.out.println(this.name);//内部类的属性
System.out.println(Person.this.name);//外部类的属性
}
}
public void method(){
//局部内部类
class AA{
}
}
{
//局部内部类
class BB{
}
}
public Person(){
//局部内部类
class CC{
}
}
}