Java基础(一)——基础语法、数组、代码块、内部类、枚举、注解、异常

阅读: 评论:0

Java基础(一)——基础语法、数组、代码块、内部类、枚举、注解、异常

Java基础(一)——基础语法、数组、代码块、内部类、枚举、注解、异常

Java基础

  • 第一章 Java概述
    • 1.1 Java语言最主要的特点
    • 1.2 Java环境搭建
      • 1.2.1 JDK、JRE、JVM
    • 1.3 第一个Java应用程序
      • 1.3.1 Java程序的开发步骤
    • 1.4 编写Java程序时应该注意的问题
  • 第二章 Java的基础语法
    • 2.1 标识符
  • 2.2 数据类型
      • 2.2.1 Java数据类型的分类
      • 2.2.2 Java的基本数据类型
      • 2.2.3 进制
      • 2.2.4 基本数据类型的转换
      • 2.2.5 特殊的数据类型转换
    • 2.3 运算符
      • 2.3.1 算术运算符
      • 2.3.2 赋值运算符
      • 2.3.3 逻辑运算符
      • 2.3.4 条件运算符
      • 2.3.5 位运算符
      • 2.3.6 运算符优先级
  • 第三章 流程控制语句结构
    • 3.1 选择结构
  • 第四章 数组
    • 4.1 数组的相关语法
      • 4.1.1数组的初始化
      • 4.1.2 数组的内存分析
    • 4.2 数组的相关算法
    • 4.3 二维数组
      • 4.3.1 二维数组的声明和初始化
  • 第五章 面向对象基础
    • 5.1 类的成员之一:属性
    • 5.2 类的成员之二:方法
      • 5.2.1 方法的参数传递机制
    • 5.3 对象数组
  • 第六章 面向对象的基本特征
    • 6.1 封装
    • 6.2 构造器
    • 6.3 关键字this
    • 6.4 包
    • 6.5 eclipse的使用
    • 6.6 面向对象的基本特征之二:继承
    • 6.7 关键字super
    • 6.8 方法的重写
    • 6.9 非静态代码块
    • 6.10 实例初始化过程
    • 6.11 面向对象的基本特征之三:多态
  • 第七章 面向对象的高级特性
    • 7.1 关键字:final
    • 7.2 关键字:native
    • 7.3 关键字:static
    • 7.4 静态代码块
    • 7.5 变量的分类与区别
    • 7.7 根父类
    • 7.8 关键字:abstract
    • 7.9 接口
    • 7.10 内部类
      • 7.10.1 匿名内部类
      • 7.10.2 静态内部类
      • 7.10.3 非静态内部类
      • 7.10.4 局部内部类
  • 第八章 枚举与注解
    • 8.1 枚举
    • 8.2 注解
  • 第九章 异常
    • 9.1 异常的类型的体系结构
    • 9.2 异常的处理
    • 9.3 手动抛出异常:throw
    • 9.4 自定义异常
    • 9.5 关于异常的几个方法

第一章 Java概述

1.1 Java语言最主要的特点

  • 特点一:面向对象

两个基本概念:类、对象

三大特性:封装、继承、多态

  • 特点二:健壮性

吸收了C/C++语言的优点,但去掉了其影响程序健壮性的部分(如指针、内存的申请与释放等),提供了一个相对安全的内存管理和访问机制

  • 特点三:跨平台性

跨平台性:通过Java语言编写的应用程序在不同的系统平台上都可以运行。“Write once , Run Anywhere”一次编写,处处运行。

原理:只要在需要运行 java 应用程序的操作系统上,先安装一个Java虚拟机 (JVM Java Virtual Machine) 即可。由JVM来负责Java程序在该系统中的运行。因为有了JVM,同一个Java 程序在三个不同的操作系统中都可以执行。这样就实现了Java 程序的跨平台性。

1.2 Java环境搭建

1.2.1 JDK、JRE、JVM

Java开发人员需要安装JDK。如果仅仅是运行Java程序,那么只需要按照JRE。

JDK(Java Development kits):Java开发工具包。

JRE(Java Runtime Environment):Java运行环境。

JVM(Java Virtual Machine):Java虚拟机。

JDK = JRE + 开发工具&#,,等)

JRE = JVM + 核心类库(常用类:String、日期时间、数学、集合、IO、网络、多线程等)

1.3 第一个Java应用程序

1.3.1 Java程序的开发步骤

三步:

1、编辑/编写源代码

要求:源文件必须是.java文件

2、编译

目的:把源文件编译为.class字节码文件(因为JVM只认识字节码)

工具&#

格式:

javac 源文件名.java

3、运行

工具&#

格式:

java 类名
java 字节码文件名

要求:可以被运行的类,必须包含main方法

1.4 编写Java程序时应该注意的问题

1、字符编码问题

当cmd命令行窗口的字符编码与.java源文件的字符编码不一致,如何解决?

解决方案一:

​ 在Notepad++等编辑器中,修改源文件的字符编码

解决方案二:

​ 在使用javac命令式,可以指定源文件的字符编码

javac -encoding utf-8 Review01.java

2、源文件名与类名一致问题?

(1)源文件名是否必须与类名一致?public呢?

如果这个类不是public,那么源文件名可以和类名不一致。

如果这个类是public,那么要求源文件名必须与类名一致。

我们建议大家,不管是否是public,都与源文件名保持一致,而且一个源文件尽量只写一个类,目的是为了好维护。

(2)一个源文件中是否可以有多个类?public呢?

一个源文件中可以有多个类,编译后会生成多个.class字节码文件。

但是一个源文件只能有一个public的类。

(3)main必须在public的类中吗?

不是。

但是后面写代码时,基本上main习惯上都在public类中。

第二章 Java的基础语法

2.1 标识符

简单的说,凡是程序员自己命名的部分都可以称为标识符。

即给类、变量、方法、包等命名的字符序列,称为标识符。

1、标识符的命名规则

(1)Java的标识符只能使用26个英文字母大小写,0-9的数字,下划线_,美元符号$

(2)不能使用Java的关键字(包含保留字)和特殊值

(3)数字不能开头

(4)不能包含空格

(5)严格区分大小写

2、标识符的命名规范

(1)见名知意

(2)类名、接口名等:每个单词的首字母都大写,形式:XxxYyyZzz,

例如:HelloWorld,String,System等

(3)变量、方法名等:从第二个单词开始首字母大写,其余字母小写,形式:xxxYyyZzz,

例如:age,name,bookName,main

(4)包名等:每一个单词都小写,单词之间使用点.分割,形式&#z,

例如:java.lang

(5)常量名等:每一个单词都大写,单词之间使用下划线_分割,形式:XXX_YYY_ZZZ,

例如:MAX_VALUE,PI

2.2 数据类型

2.2.1 Java数据类型的分类

1、基本数据类型

​ 8种:整型系列(byte,short,int,long)、浮点型(float,double)、单字符型(char)、布尔型(boolean)

2、引用数据类型

​ 类、接口、数组、枚举…

2.2.2 Java的基本数据类型

1、整型系列

(1)byte:字节类型

占内存:1个字节

存储范围:-128~127

(2)short:短整型类型

占内存:2个字节

存储范围:-32768~32767

(3)int:整型

占内存:4个字节

存储范围:-2的31次方 ~ 2的31次方-1

(4)long:整型

占内存:8个字节

存储范围:-2的63次方 ~ 2的63次方-1

注意:如果要表示某个常量数字它是long类型,那么需要在数字后面加L

2、浮点型系列(小数)

(1)float:单精度浮点型

占内存:4个字节

精度:科学记数法的小数点后6~7位

注意:如果要表示某个常量数字是float类型,那么需要在数字后面加F或f

(2)double:双精度浮点型

占内存:8个字节

精度:科学记数法的小数点后15~16位

3、单字符类型

char:字符类型

占内存:2个字节

Java中使用的字符集:Unicode编码集

字符的三种表示方式:

(1)‘一个字符’

例如:‘A’,‘0’,‘尚’

(2)转义字符

n:换行
r:回车
t:Tab键
\:
":”
':
b:删除键Backspace

(3)u字符的Unicode编码值的十六进制型

例如:u5c1a代表’尚’

4、布尔类型

boolean:只能存储true或false

2.2.3 进制

1、请分别用四种类型的进制来表示10,并输出它的结果:(了解)

(1)十进制:正常表示

System.out.println(10);

(2)二进制:0b或0B开头

System.out.println(0B10);

(3)八进制:0开头

System.out.println(010);

(4)十六进制:0x或0X开头

System.out.println(0X10);

2、为什么byte是-128~127?(理解)

1个字节:8位

0000 0001 ~ 0111 111 ==> 1~127

1000 0001 ~ 1111 1111 ==> -127 ~ -1

0000 0000 ==>0

1000 0000 ==> -128(特殊规定)

*解释:*计算机数据的存储(了解)

计算机数据的存储使用二进制补码形式存储,并且最高位是符号位,1是负数,0是正数。

规定:正数的补码与反码、原码一样,称为三码合一;

负数的补码与反码、原码不一样:

负数的原码:把十进制转为二进制,然后最高位设置为1

负数的反码:在原码的基础上,最高位不变,其余位取反(0变1,1变0)

负数的补码:反码+1

例如:byte类型(1个字节,8位)

25 ==> 原码 0001 1001 ==> 反码 0001 1001 -->补码 0001 1001

-25 ==>原码 1001 1001 ==> 反码1110 0110 ==>补码 1110 0111

底层是用加法代替减法:-128==》-127-1==》-127+(-1)

​ -127- -1 ==> -127 + 1

2.2.4 基本数据类型的转换

1、自动类型转换

(1)当把存储范围小的值(常量值、变量的值、表达式计算的结果值)赋值给了存储范围大的变量时,

byte->short->int->long->float->double

​ char->

int i = 'A';//char自动升级为int
double d = 10;//int自动升级为double

(2)当存储范围小的数据类型与存储范围大的数据类型一起混合运算时,会按照其中最大的类型运算

int i = 1;
byte b = 1;
double d = 1.0;double sum = i + b + d;//混合运算,升级为double

(3)当byte,short,char数据类型进行算术运算时,按照int类型处理

byte b1 = 1;
byte b2 = 2;
byte b3 = (byte)(b1 + b2);//b1 + b2自动升级为intchar c1 = '0';
char c2 = 'A';
System.out.println(c1 + c2);//113 

(4)boolean类型不参与

2、强制类型转换

(1)当把存储范围大的值(常量值、变量的值、表达式计算的结果值)赋值给了存储范围小的变量时,需要强制类型转换

double->float->long->int->short->byte

​ ->char

提示:有风险,可能会损失精度或溢出

double d = 1.2;
int num = (int)d;//损失精度int i = 200;
byte b = (byte)i;//溢出

(2)boolean类型不参与

(3)当某个值想要提升数据类型时,也可以使用强制类型转换

int i = 1;
int j = 2;
double shang = (double)i/j;

提示:这个情况的强制类型转换是没有风险的。

2.2.5 特殊的数据类型转换

1、任意数据类型的数据与String类型进行“+”运算时,结果一定是String类型

System.out.println("" + 1 + 2);//12

2、但是String类型不能通过强制类型()转换,转为其他的类型

String str = "123";
int num = (int)str;//错误的

2.3 运算符

1、按照操作数个数的分类:

(1)一元运算符:操作数只有一个

例如:正号(+),负号(-),自增(++),自减(–),逻辑非(!),按位取反(~)

(2)二元运算符:操作数有两个

例如:加(+),减(-),乘(*),除(/),模(%)

​ 大于(>),小于(<),大于等于(>=),小于等于(<=),等于(==),不等于(!=)

​ 赋值(=,+=,-=,*=,/=,%=,>>=,<<=。。。)

​ 逻辑与(&),逻辑或(|),逻辑异或(^),短路与(&&),短路或(||)

​ 左移(<<),右移(>>),无符号右移(>>>),按位与(&),按位或(|),按位异或(^)

(3)三元运算符:操作数三个

例如: ? :

2、Java基本数据类型的运算符:

(1)算术运算符

(2)赋值运算符

(3)比较运算符

(4)逻辑运算符

(5)条件运算符

(6)位运算符(难)

2.3.1 算术运算符

加法:+

减法:-

乘法:*

除法:/

注意:整数与整数相除,只保留整数部分

取模:% 取余

注意:取模结果的正负号只看被模数

正号:+

负号:-

自增:++

自减:–

原则:自增与自减

++/–在前的,就先自增/自减,后取值

++/–在后的,就先取值,后自增/自减

整个表达式的扫描,是从左往右扫描,如果后面的先计算的,那么前面的就暂时先放到“操作数栈”中

代码示例:

int i = 1;
i++;//i=2int j = 1;
++j;//j=2int a = 1;
int b = a++;//(1)先取a的值“1”放操作数栈(2)a再自增,a=2(3)再把操作数栈中的"1"赋值给b,b=1int m = 1;
int n = ++m;//(1)m先自增,m=2(2)再取m的值“2”放操作数栈(3)再把操作数栈中的"2"赋值给n,n=1int i = 1;
int j = i++ + ++i * i++;
/*
从左往右加载
(1)先算i++
①取i的值“1”放操作数栈
②i再自增 i=2
(2)再算++i
①i先自增 i=3
②再取i的值“3”放操作数栈
(3)再算i++
①取i的值“3”放操作数栈
②i再自增 i=4
(4)先算乘法
用操作数栈中3 * 3 = 9,并把9压会操作数栈
(5)再算求和
用操作数栈中的 1 + 9 = 10
(6)最后算赋值
j = 10
*/

2.3.2 赋值运算符

基本赋值运算符:=

扩展赋值运算符:+=,-=,*=,/=,%=…

注意:所有的赋值运算符的=左边一定是一个变量

扩展赋值运算符=右边的计算结果的类型如果比左边的大的话会强制类型转换,所以结果可能有风险。

扩展赋值运算符的计算:(1)赋值最后算(2)加载数据的顺序是把左边的变量的值先加载,再去与右边的表达式进行计算

int i = 1;
int j = 5;
j *= i++ + j++;//j = j *(i++ + j++);
/*
(1)先加载j的值“5”
(2)在计算i++
①先加载i的值“1”
②再i自增,i=2
(3)再计算j++
①先加载j的值"5"
②再j自增,j=6
(4)算  加法
i + 5 = 6
(5)算乘法
5 * 6 = 30
(6)赋值
j = 30
*/

2.3.3 逻辑运算符

逻辑运算符的操作数必须是布尔值,结果也是布尔值

逻辑与:&
运算规则:只有左右两边都为true,结果才为true。
例如:true & true 结果为true
false & true 结果为false
true & false 结果为false
false & false 结果为false
逻辑或:|
运算规则:只要左右两边有一个为true,结果就为true。
例如:true | true 结果为true
false | true 结果为true
true | false 结果为true
false | false 结果为false
逻辑异或:^
运算规则:只有左右两边不同,结果才为true。
例如:true ^ true 结果为false
false ^ true 结果为true
true ^ false 结果为true
false ^ false 结果为false

逻辑非:!
运算规则:布尔值取反
例如:!true 为false
!false 为true

短路与:&&
运算规则:只有左右两边都为true,结果才为true。
例如:true & true 结果为true
true & false 结果为false
false & ? 结果就为false
它和逻辑与不同的是当&&左边为false时,右边就不看了。

短路或:||
运算规则:只要左右两边有一个为true,结果就为true。
例如:true | ? 结果为treu
false | true 结果为true
false | false 结果为false
它和逻辑或不同的是当||左边为true时,右边就不看了。

开发中一般用短路与和短路或比较多

面试题:&& 和 &的区别?

&&当左边为false,右边不计算

&不管左边是true还是false,右边都要计算

2.3.4 条件运算符

? :

语法格式:

条件表达式 ? 结果表达式1 : 结果表达式2

运算规则:

整个表达式的结果:当条件表达式为true时,就取结果表达式1的值,否则就取结果表达式2的值

代码示例:

(1)boolean类型
boolean marry = true;
System.out.println(marry? "已婚" : "未婚");(2)求最值
int i = 3;
int j = 5;
int max = i>=j ? i : j;
//当i>=j时,max就赋值为i的值,否则就赋值为j的值

2.3.5 位运算符

左移:<<

​ 运算规则:左移几位就相当于乘以2的几次方

右移:>>

​ 运算规则:右移几位就相当于除以2的几次方

无符号右移:>>>

​ 运算规则:往右移动后,左边空出来的位直接补0,不看符号位

按位与:&

​ 运算规则:

​ 1 & 1 结果为1

​ 1 & 0 结果为0

​ 0 & 1 结果为0

​ 0 & 0 结果为0

按位或:|

​ 运算规则:

​ 1 | 1 结果为1

​ 1 | 0 结果为1

​ 0 | 1 结果为1

​ 0 & 0 结果为0

按位异或:^

​ 运算规则:

​ 1 ^ 1 结果为0

​ 1 ^ 0 结果为1

​ 0 ^ 1 结果为1

​ 0 ^ 0 结果为0

按位取反:~

​ 运算规则:~0就是1

​ ~1就是0

如何区分&,|,^是逻辑运算符还是位运算符?

如果操作数是boolean类型,就是逻辑运算符,如果操作数是整数,那么就位运算符。

2.3.6 运算符优先级

提示说明:

(1)表达式不要太复杂

(2)先算的使用()

第三章 流程控制语句结构

3.1 选择结构

语法格式:

switch(表达式){case 常量值1:语句块1;【break;】case 常量值2:语句块2;【break;】   。。。【default:语句块n+1;【break;】】
}

执行过程:

(1)入口

①当switch(表达式)的值与case后面的某个常量值匹配,就从这个case进入;

②当switch(表达式)的值与case后面的所有常量值都不匹配,寻找default分支进入;

(2)一旦从“入口”进入switch,就会顺序往下执行,直到遇到“出口”

(3)出口

①自然出口:遇到了switch的结束}

②中断出口:遇到了break等

注意:

(1)switch(表达式)的值的类型,只能是:4种基本数据类型(byte,short,int,char),两种引用数据类型(枚举、String)

(2)case后面必须是常量值,而且不能重复

示例代码:

int month = 4;
switch(month){case 3:case 4:case 5:System.out.println("春季");break;case 6:case 7:case 8:System.out.println("夏季");break;case 9:case 10:case 11:System.out.println("秋季");break;case 12:case 1:case 2:System.out.println("冬季");break;default:System.out.println("输入有误!");
}

第四章 数组

4.1 数组的相关语法

4.1.1数组的初始化

初始化的目的:(1)确定数组的长度(2)为元素赋值

两种初始化方式:

1、动态初始化

语法格式:

//指定数组长度
数组名 = new 元素的数据类型[长度];//为元素赋值
数组名[下标] = 值; //这个值可以是个常量值,也可以是个表达式的计算结果,也可以是键盘输入的//如果每个元素的赋值比较有规律,通常使用for循环赋值
for(int i=0; i<长度; i++){数组名[下标] = 值;
}

问:如果只指定数组长度,没有为元素手动赋值,那么元素有值吗?

有默认值

(1)基本数据类型

​ byte,short,int,long:0

​ float,double:0.0

​ char:u0000

​ boolean:false

(2)引用数据类型

​ 统统都是null

2、静态初始化

语法格式:

数组名 = new 元素的数据类型[]{值列表};//int[] arr = new int[5]{1,2,3,4,5};//错误的//更简洁
//当声明与静态初始化一起完成时,可以简化
元素的数据类型[] 数组名 = {值列表};

适用场合:

​ 当数组的元素是已知的有限个时,可以使用静态初始化。

示例代码:

String[] weeks = {"monday","tuesday","wednesday","thursday","friday","saturday","sunday"};int[] daysOfMonths = {31,28,31,30,31,30,31,31,30,31,30,31};char[] letters = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};

4.1.2 数组的内存分析

元素是基本数据类型的一维数组内存分析:

int[] arr = {1,2,3,4,5};

int[] arr = new int[5];
for(int i=0; i<arr.length; i++){arr[i] = i+1;
}

4.2 数组的相关算法

int[] arr = {1,2,3,4,5,6,7,8,9};//(1)计算要交换的次数:  次数 = arr.length/2
//(2)首尾交换
for(int i=0; i<arr.length/2; i++){//循环的次数就是交换的次数//首  与  尾交换int temp = arr[i];arr[i] = arr[arr.length-1-i];arr[arr.length-1-i] = temp;
}//(3)遍历显示
for(int i=0; i<arr.length; i++){System.out.println(arr[i]);
}

4.3 二维数组

二维数组的标记:[][]

4.3.1 二维数组的声明和初始化

1、二维数组的声明

  //推荐元素的数据类型[][] 二维数组的名称;//不推荐元素的数据类型  二维数组名[][];//不推荐元素的数据类型[]  二维数组名[];

面试:

int[] x, y[];
//x是一维数组,y是二维数组

2、二维数组的初始化

(1)静态初始化

二维数组名 = new 元素的数据类型[][]{{第一行的值列表}, {第二行的值列表},...{第n行的值列表}};//如果声明与静态初始化一起完成
元素的数据类型[][] 二维数组的名称 = {{第一行的值列表}, {第二行的值列表},...{第n行的值列表}};

(2)动态初始化(不规则:每一行的列数可能不一样)

//(1)先确定总行数
二维数组名 = new 元素的数据类型[总行数][];//(2)再确定每一行的列数
二维数组名[行下标] = new 元素的数据类型[该行的总列数];//(3)再为元素赋值
二维数组名[行下标][列下标] = 值;

(3)动态初始化(规则:每一行的列数是相同的)

//(1)确定行数和列数
二维数组名 = new 元素的数据类型[总行数][每一行的列数];//(2)再为元素赋值
二维数组名[行下标][列下标] = 值;

第五章 面向对象基础

5.1 类的成员之一:属性

1、如何声明属性?

【修饰符】 class 类名{【修饰符】 数据类型  属性名;    //属性有默认值【修饰符】 数据类型  属性名 = 值; //属性有初始值
}

说明:属性的类型可以是Java的任意类型,包括基本数据类型、引用数据类型(类、接口、数组等)

总结:Java的数据类型

(1)基本数据类型

byte,short,int,long,float,double,char,boolean

(2)引用数据类型

①类:

​ 例如:String、Student、Circle、System、Scanner、Math…

②接口:

③数组:

​ 例如:int[],String[],char[],int[][]

int[] arr = new int[5];
这里把int[]看成数组类型,是一种引用数据类型,右边赋值的是一个数组的对象元素的数据类型:int
数组的数据类型:int[]

2、属性的特点

(1)属性有默认值

基本数据类型:

​ byte,short,int,long:0

​ float,double:0.0

​ char:u0000

​ boolean:false

引用数据类型:

​ null

(2)每一个对象的属性是独立,互不干扰

3、对象属性的内存图

class Student{String name;char gender = '男';//显式赋值
}class TestStudent{public static void main(String[] args){Student s1 = new Student();System.out.println("姓名:" + s1.name);//nullSystem.out.println("性别:" + s1.gender);//男s1.name = "小薇";s1.gender = '女';System.out.println("姓名:" + s1.name);//小薇System.out.println("性别:" + s1.gender);//女Student s2 = new Student();System.out.println("姓名:" + s2.name);//nullSystem.out.println("性别:" + s2.gender);//男}
}

5.2 类的成员之二:方法

5.2.1 方法的参数传递机制

Java中方法的参数传递机制:值传递

(1)形参是基本数据类型时,实参给形参传递数据值,是copy的形式,形参对值的修改不影响实参。
(2)形参是引用数据类型时,实参给形参传递地址值,形参对对象的属性的修改,会影响实参对象的属性值,因为此时形参和实参就是指向同一个对象。
示例代码:

class Test{public static void swap(int a, int b){int temp = a;a = b;b = temp;}public static void main(String[] args){int x = 1;int y = 2;swap(x,y);//调用完之后,x与y的值不变}
}

示例代码:

class Test{public static void change(MyData my){my.num *= 2;}public static void main(String[] args){MyData m = new MyData();m.num = 1;change(m);//调用完之后,m对象的num属性值就变为2}
}class MyData{int num;
}

陷阱1:

/*
陷阱1:在方法中,形参 = 新new对象,那么就和实参无关了
*/
class Test{public static void change(MyData my){my = new MyData();//形参指向了新对象my.num *= 2;}public static void main(String[] args){MyData m = new MyData();m.num = 1;change(m);//调用完之后,m对象的num属性值仍然为1}
}class MyData{int num;
}

陷阱2:见字符串和包装类部分

5.3 对象数组

一维数组:

1、元素是基本数据类型

2、元素是引用数据类型,也称为对象数组,即数组的元素是对象

注意:对象数组,首先要创建数组对象本身,即确定数组的长度,然后再创建每一个元素对象,如果不创建,数组的元素的默认值就是null,所以很容易出现空指针异常NullPointerException。

示例代码:

class MyDate{int year;int month;int day;
}
class Test{public static void main(String[] args){MyDate[] arr = new MyDate[3];//创建数组对象本身,指定数组的长度for(int i=0; i<arr.length; i++){arr[i] = new MyDate();//每一个元素要创建对象arr[i].year = 1990 + i;arr[i].month = 1 + i;arr[i].day = 1 + i;}}
}

对象数组的内存图:

第六章 面向对象的基本特征

面向对象的基本特征:

1、封装

2、继承

3、多态

6.1 封装

1、好处:

(1)隐藏实现细节,方便使用者使用

(2)安全,可以控制可见范围

2、如何实现封装?

通过权限修饰符

面试题:请按照可见范围从小到大(从大到小)列出权限修饰符?

修饰符本类本包其他包的子类任意位置
private×××
缺省××
protected×
public

权限修饰符可以修饰什么?

类(类、接口等)、属性、方法、构造器、内部类

类(外部类):public和缺省

属性:4种

方法:4种

构造器:4种

内部类:4种

6.2 构造器

3、构造器的特点:

(1)所有的类都有构造器

(2)如果一个类没有显式/明确的声明一个构造器,那么编译器将会自动添加一个默认的无参构造

(3)如果一个类显式/明确的声明了构造器,那么编译器将不再自动添加默认的无参构造,如果需要,那么就需要手动添加

(4)构造器的名称必须与类名相同

(5)构造器没有返回值类型

(6)构造器可以重载

示例代码:

public class Circle{private double radius;public Circle(){}public Circle(double r){radius = r;//为radius赋值}
}

6.3 关键字this

1、this关键字:

意思:当前对象

(1)如果出现在构造器中:表示正在创建的对象

(2)如果出现在成员方法中:表示正在调用这个方法的对象

2、this的用法:

(1)this()或this(实参列表)

this()表示调用本类的无参构造

this(实参列表)表示调用本类的有参构造

this()或this(实参列表)要么没有,要么必须出现在构造器的首行

3、成员变量与局部变量的区别?

这里只讨论实例变量(关于类变量见static部分)

(1)声明的位置不同

成员变量:类中方法外

局部变量:方法中或代码中

​ ①方法的形参列表

​ ②方法体中局部变量

​ ③代码块中的局部变量

(2)运行时在内存中的存储位置不同

成员变量:堆

局部变量:栈

基本数据类型的变量在栈中,引用数据类型的变量在堆中:不准确

(3)修饰符

成员变量:有很多修饰符,例如:权限修饰符

局部变量:不能加权限修饰符,唯一的能加的是final

(4)初始化

成员变量:有默认值

局部变量:没有默认值,必须手动初始化

(5)生命周期

成员变量:随着对象的创建而创建,随着对象被回收而消亡,即与对象同生共死。每一个对象都是独立的。

局部变量:方法调用时才分配,方法运行结束就没有了。每一次方法调用,都是独立的

6.4 包

1、包的作用:

(1)可以避免类重名

有了包之后,类的全名称就变为:包.类名

(2)分类组织管理众多的类

例如:java.lang包,java.util包,java.io包…

(3)可以控制某些类型或成员的可见范围

如果某个类型或者成员的权限修饰缺省的话,那么就仅限于本包使用

3、包的命名规范和习惯:
(1)所有单词都小写,每一个单词之间使用.分割
(2)习惯用公司的域名倒置

例如:;

建议大家取包名时不要使用“"包

4、使用其他包的类:

前提:被使用的类或成员的权限修饰符是>缺省的

(1)使用类型的全名称

例如:java.util.Scanner input = new java.util.Scanner(System.in);

(2)使用import 语句之后,代码中使用简名称

5、import语句

import 包.类名;
import 包.*;

注意:当使用两个不同包的同名类时,例如:java.util.Date和java.sql.Date。

一个使用全名称,一个使用简名称

示例代码:

package st;import java.util.Scanner;public class Test{public static void main(String[] args){Scanner input = new Scanner(System.in);}
}

6.5 eclipse的使用

1、eclipse管理项目和代码的结构

workspace --> project --> 包–>类…

一个工作空间可以有多个项目。

6.6 面向对象的基本特征之二:继承

1、为什么要继承?继承的好处?(理解)

(1)代码的复用

(2)代码的扩展

2、如何实现继承?

语法格式:

【修饰符】 class 子类  extends 父类{}

3、继承的特点

(1)子类会继承父类的所有特征(属性、方法)
可以直接使用父类public、protected方法,就像是自己的方法

但是,私有的在子类中是不能直接使用的

(2)子类不会继承父类的构造器

因为,父类的构造器是用于创建父类的对象的

(3)子类的构造器中又必须去调用父类的构造器

在创建子类对象的同时,为从父类继承的属性进行初始化用,可以借助父类的构造器中的代码为属性赋值。

(4)Java只支持单继承:一个子类只能有一个“直接”父类

(5)Java又支持多层继承:父类还可以有父类,特征会代代相传

(6)一个父类可以同时拥有很多个子类

6.7 关键字super

super关键字:引用父类的,找父类的xx

用法:

(1)super.属性

当子类声明了和父类同名的成员变量时,那么如果要表示某个成员变量是父类的,那么可以加“super.”

(2)super.方法

当子类重写了父类的方法,又需要在子类中调用父类被重写的方法,可以使用"super."

(3)super()或super(实参列表)

super():表示调用父类的无参构造

super(实参列表):表示调用父类的有参构造

注意:

(1)如果要写super()或super(实参列表),必须写在子类构造器的首行

(2)如果子类的构造器中没有写:super()或super(实参列表),那么默认会有 super()

(3)如果父类没有无参构造,那么在子类的构造器的首行“必须”写super(实参列表)

6.8 方法的重写

1、方法的重写(Override)

当子类继承了父类的方法时,又觉得父类的方法体的实现不适合于子类,那么子类可以选择进行重写。

2、方法的重写的要求

(1)方法名:必须相同

(2)形参列表:必须相同

(3)修饰符

​ 权限修饰符: >=

(4)返回值类型

​ 如果是基本数据类型和void:必须相同

​ 如果是引用数据类型:<=

​ 在Java中我们认为,在概念范围上:子类 <父类

3、重载(Overload)与重写(Override)的区别

​ 重载(Overload):在同一个类中,方法名相同,形参列表不同,和返回值类型无关的两个或多个方法。

(参数必须不相同,在此基础上返回值可以不同可以相同,但如果参数相同,返回值相不相同都会报错)

​ 重写(Override):在父子类之间。对方法签名的要求见上面。

特殊的重载:

public class TestOverload {public static void main(String[] args) {B b = new B();//b对象可以调用几个a方法b.a();b.a("");//从b对象同时拥有两个方法名相同,形参不同的角度来说,算是重载}
}
class A{public void a(){//...}
}
class B extends A{public void a(String str){}
}

6.9 非静态代码块

1、语法格式

【修饰符】 class 类名{{非静态代码块}
}

2、作用

目的:在创建的过程中,为对象属性赋值,协助完成实例初始化的过程

3、什么时候执行?

(1)每次创建对象时都会执行

(2)优先于构造器执行

6.10 实例初始化过程

1、概念描述

  • 实例初始化过程:实例对象创建的过程

  • 实例初始化方法:实例对象创建时要执行的方法

  • 实例初始化方法的由来:它是有编译器编译生成的

  • 实例初始化方法的形式:()或(形参列表)

  • 实例初始化方法的构成:

    ①属性的显式赋值代码

    ②非静态代码块的代码

    ③构造器的代码

    其中

    ①和②按顺序执行,从上往下

    ③在①和②的后面

因此一个类有几个构造器,就有几个实例初始化方法。

2、单个类实例初始化方法

示例代码:

class Demo{{System.out.println("非静态代码块1");}private String str = assign();//调用方法,来为str进行显式赋值public Demo(){System.out.println("无参构造");}public Demo(String str){this.str = str;System.out.println("有参构造");}{System.out.println("非静态代码块2");}public String assign(){System.out.println("assign方法");return "hello";}
}

图解:

3、父子类的实例初始化

注意:

(1)原先super()和super(实参列表)说是调用父类的构造器,现在就要纠正为调用父类的实例初始化方法了

(2)原先super()和super(实参列表)说是必须在子类构造器的首行,现在要纠正为必须在子类实例初始化方法的首行

结论:

(1)执行顺序是先父类实例初始化方法,再子类实例初始化方法

(2)如果子类重写了方法,通过子类对象调用,一定是执行重写过的方法

示例代码:

class Ba{private String str = assign();{System.out.println("(1)父类的非静态代码块");}public Ba(){System.out.println("(2)父类的无参构造");}public String assign(){System.out.println("(3)父类的assign()");return "ba";}
}
class Er extends Ba{private String str = assign();{System.out.println("(4)子类的非静态代码块");}public Er(){//super()  ==>调用父类的实例初始化方法,而且它在子类实例初始化方法的首行System.out.println("(5)子类的无参构造");}public String assign(){System.out.println("(6)子类的assign()");return "er";}
}
class Test{public static void main(String[] args){new Er();//612645}
}

图解:

6.11 面向对象的基本特征之三:多态

1、多态:

语法格式:

父类 引用/变量 = 子类的对象;

2、前提:

(1)继承

(2)方法的重写

(3)多态引用

3、现象:

​ 编译时看左边/“父类”,运行时看右边/“子类”。

​ 编译时,因为按父类编译,那么只能父类有的方法,子类扩展的方法是无法调用的;

​ 执行时一定是运行子类重写的过的方法体。

示例代码:

class Person{public void eat(){System.out.println("吃饭");}public void walk(){System.out.println("走路");}
}
class Woman extends Person{public void eat(){System.out.println("细嚼慢咽的吃饭");}public void walk(){System.out.println("婀娜多姿走路");}public void shop(){System.out.println("买买买...");}
}
class Man extends Person{public void eat(){System.out.println("狼吞虎咽的吃饭");}public void walk(){System.out.println("大摇大摆的走路");}public void smoke(){System.out.println("吞云吐雾");}
}
class Test{public static void main(String[] args){Person p = new Woman();//多态引用p.eat();//执行子类重写p.walk();//执行子类重写//p.shop();//无法调用}
}

4、应用:

(1)多态参数:形参是父类,实参是子类对象

(2)多态数组:数组元素类型是父类,元素存储的是子类对象

示例代码:多态参数

class Test{public static void main(String[] args){test(new Woman());//实参是子类对象test(new Man());//实参是子类对象}public static void test(Person p){//形参是父类类型p.eat();p.walk();}
}

示例代码:多态数组

class Test{public static void main(String[] args){Person[] arr = new Person[2];//多态数组arr[0] = new Woman();arr[1] = new Man();for(int i=0; i<arr.length; i++){all[i].eat();all[i].walk();}}
}

5、向上转型与向下转型:父子类之间的转换

(1)向上转型:自动类型转换

​ 当把子类的对象赋值给父类的变量时(即多态引用时),在编译时,这个对象就向上转型为父类。此时就看不见子类“特有、扩展”的方法。

(2)向下转型:强制转换。有风险,可能会报ClassCastException异常。

​ 当需要把父类的变量赋值给一个子类的变量时,就需要向下转型。

要想转型成功,必须保证该变量中保存的对象的运行时类型是<=强转的类型

示例代码:

class Person{//方法代码省略...
}
class Woman extends Person{//方法代码省略...
}
class ChineseWoman extends Woman{//方法代码省略...
}
 public class Test{public static void main(String[] args){//向上转型Person p1 = new Woman();//向下转型Woman m = (Woman)p1; //p1变量中实际存储的对象就是Woman类型,和强转的Woman类型一样//向上转型Person p2 = new ChineseWoman();//向下转型Woman w2 = (Woman) p2; //p2变量中实际存储的对象是ChineseWoman类型,强制的类型是Woman,ChineseWoman<Woman类型     }}

6、instanceof

表达式语法格式:

对象/变量  instanceof  类型

运算结果:true 或 false

作用:

用来判断这个对象是否属于这个类型,或者说,是否是这个类型的对象或这个类型子类的对象

示例代码:

class Person{//方法代码省略...
}
class Woman extends Person{//方法代码省略...
}
class ChineseWoman extends Woman{//方法代码省略...
}
 public class Test{public static void main(String[] args){Person p = new Person();Woman w = new Woman();ChineseWoman c = new ChineseWoman();if(p instanceof Woman){//false}if(w instanceof Woman){//true}if(c instanceof Woman){//true}}}

第七章 面向对象的高级特性

修饰符的学习围绕三个问题:

(1)单词的意思

(2)可以修饰什么?

(3)用它修饰后有什么不同?

7.1 关键字:final

final:最终的

用法:

(1)修饰类(包括外部类、内部类类)

表示这个类不能被继承,没有子类

(2)修饰方法

表示这个方法不能被重写

(3)修饰变量(成员变量(类变量、实例变量),局部变量)

表示这个变量的值不能被修改

注意:如果某个成员变量用final修饰后,也得手动赋值,而且这个值一旦赋完,就不能修改了,即没有set方法

7.2 关键字:native

native:本地的,原生的
用法:

​ 只能修饰方法

​ 表示这个方法的方法体代码不是用Java语言实现的。

​ 但是对于Java程序员来说,可以当做Java的方法一样去正常调用它,或者子类重写它。

JVM内存的管理:

方法区:类的信息、常量、静态变量、动态编译生成的字节码信息

虚拟机栈:Java语言实现的方法的局部变量

本地方法栈:非Java语言实现的方法的局部变量,即native方法执行时的内存区域

堆:new出来的对象

程序计数器:记录每一个线程目前执行到哪一句指令

7.3 关键字:static

static:静态的

用法:

1、成员方法:我们一般称为静态方法或类方法

(1)不能被重写

(2)被使用

本类中:其他方法中可以直接使用它

其他类中:可以使用“类名.方法"进行调用,也可以使用"对象名.方法",推荐使用“类名.方法"

(3)在静态方法中,我们不能出现:this,super,非静态的成员

2、成员变量:我们一般称为静态变量或类变量

(1)静态变量的值是该类所有对象共享的

(2)静态变量存储在方法区

(3)静态变量对应的get/set也是静态的

(4)静态变量与局部变量同名时,就可以使用“类名.静态变量"进行区分

3、内部类:

4、代码块:静态代码块

5、静态导入(JDK1.5引入)

没有静态导入

package com.atguigu.utils;public class Utils{public static final int MAX_VALUE = 1000;public static void test(){//...}
}
package st;import com.atguigu.utils;public class Test{public static void main(String[] args){System.out.println(Utils.MAX_VALUE);st();}
}

使用静态导入

package com.atguigu.utils;public class Utils{public static final int MAX_VALUE = 1000;public static void test(){//...}
}
package st;import static com.atguigu.utils.Utils.*;public class Test{public static void main(String[] args){System.out.println(MAX_VALUE);test();}
}

7.4 静态代码块

1、语法格式:

【修饰符】 class 类名{static{静态代码块;}
}

2、作用:

协助完成类初始化,可以为类变量赋值。

3、类初始化()

类的初始化有:

①静态变量的显式赋值代码

②静态代码块中代码

其中①和②按顺序执行

注意:类初始化方法,一个类只有一个

4、类的初始化的执行特点:

(1)每一个类的()只执行一次

(2)如果一个子类在初始化时,发现父类也没有初始化,会先初始化父类

(3)如果既要类初始化又要实例化初始化,那么一定是先完成类初始化的

7.5 变量的分类与区别

1、变量按照数据类型分:

(1)基本数据类型的变量,里面存储数据值

(2)引用数据类型的变量,里面存储对象的地址值

int a = 10;//a中存储的是数据值Student stu = new Student();//stu存储的是对象的地址值
int[] arr = new int[5];//arr存储的是数组对象的地址值
String str = "hello";//str存储的是"hello"对象的地址值

2、变量按照声明的位置不同:

(1)成员变量

(2)局部变量

3、成员变量与局部变量的区别

(1)声明的位置不同

成员变量:类中方法外

局部变量:①方法的()中,即形参 ②方法体的{}的局部变量③代码块{}中

(2)存储的位置不同

成员变量:

​ 如果是静态变量(类变量),在方法区中

​ 如果是非静态的变量(实例变量),在堆中

局部变量:栈

(3)修饰符不同

成员变量:4种权限修饰符、static、final。。。。

局部变量:只有final

(4)生命周期

成员变量:

​ 如果是静态变量(类变量),和类相同

​ 如果是非静态的变量(实例变量),和所属的对象相同,每一个对象是独立

局部变量:每次执行都是新的

(5)作用域

成员变量:

​ 如果是静态变量(类变量),在本类中随便用,在其他类中使用“类名.静态变量"

​ 如果是非静态的变量(实例变量),在本类中只能在非静态成员中使用,在其他类中使用“对象名.非静态的变量"

局部变量:有作用域

7.7 根父类

1、java.lang.Object类是类层次结构的根父类。包括数组对象。

(1)Object类中声明的所有的方法都会被继承到子类中,那么即所有的对象,都拥有Object类中的方法

(2)每一个对象的创建,最终都会调用到Object实例初始化方法()

(3)Object类型变量、形参、数组,可以存储任意类型的对象

2、Object类的常用方法

(1)public String toString():

①默认情况下,返回的是“对象的运行时类型 @ 对象的hashCode值的十六进制形式"

②通常是建议重写,如果在eclipse中,可以用Alt +Shift + S–>Generate toString()

③如果我们直接System.out.println(对象),默认会自动调用这个对象的toString()

(2)public final Class<?> getClass():获取对象的运行时类型

(3)protected void finalize():当对象被GC确定为要被回收的垃圾,在回收之前由GC帮你调用这个方法。而且这个方法只会被调用一次。子类可以选择重写。

(4)public int hashCode():返回每个对象的hash值。

规定:①如果两个对象的hash值是不同的,那么这两个对象一定不相等;

​ ②如果两个对象的hash值是相同的,那么这两个对象不一定相等。

主要用于后面当对象存储到哈希表等容中时,为了提高性能用的。

(5)public boolean equals(Object obj):用于判断当前对象this与指定对象obj是否“相等”

①默认情况下,equals方法的实现等价于与“==”,比较的是对象的地址值

②我们可以选择重写,重写有些要求:

A:如果重写equals,那么一定要一起重写hashCode()方法,因为规定:

​ a:如果两个对象调用equals返回true,那么要求这两个对象的hashCode值一定是相等的;

​ b:如果两个对象的hashCode值不同的,那么要求这个两个对象调用equals方法一定是false;

​ c:如果两个对象的hashCode值相同的,那么这个两个对象调用equals可能是true,也可能是false

B:如果重写equals,那么一定要遵循如下几个原则:

​ a:自反性:x.equals(x)返回true

​ b:传递性:x.equals(y)为true, y.equals(z)为true,然后x.equals(z)也应该为true

​ c:一致性:只要参与equals比较的属性值没有修改,那么无论何时调用结果应该一致

​ d:对称性:x.equals(y)与y.equals(x)结果应该一样

​ e:非空对象与null的equals一定是false

(6)protected native Object clone() throws CloneNotSupportedException;
在Java中本方法为native,即不是在Java中实现的。

但我们可以根据这个方法引出深拷贝和浅拷贝两个概念

值得注意的是,拷贝对象时,对象中的String类型属性和基本类型属性一样直接赋值即可,因为对象中有String属性的话,new对象会自动new 一个string ,所以把String当作基本类型就好

Java实现深拷贝

7.8 关键字:abstract

1、什么时候会用到抽象方法和抽象类?

当声明父类的时候,在父类中某些方法的方法体的实现不能确定,只能由子类决定。但是父类中又要体现子类的共同的特征,即它要包含这个方法,为了统一管理各种子类的对象,即为了多态的应用。

那么此时,就可以选择把这样的方法声明为抽象方法。如果一个类包含了抽象方法,那么这个类就必须是个抽象类。

2、抽象类的语法格式

【权限修饰符】 abstract class 类名{}
【权限修饰符】 abstract class 类名 extends 父类{}

3、抽象方法的语法格式

【其他修饰符】 abstract 返回值类型  方法名(【形参列表】);

抽象方法没有方法体

4、抽象类的特点

(1)抽象类不能直接实例化,即不能直接new对象

(2)抽象类就是用来被继承的,那么子类继承了抽象类后,必须重写所有的抽象方法,否则这个子类也得是抽象类

(3)抽象类也有构造器,这个构造的作用不是创建抽象类自己的对象用的,给子类在实例化过程中调用;

(4)抽象类也可以没有抽象方法,那么目的是不让你创建对象,让你创建它子类的对象

(5)抽象类的变量与它子类的对象也构成多态引用

5、不能和abstract一起使用的修饰符?

(1)final:和final不能一起修饰方法和类

(2)static:和static不能一起修饰方法

(3)native:和native不能一起修饰方法

(4)private:和private不能一起修饰方法

7.9 接口

1、接口的概念

接口是一种标准。注意关注行为标准(即方法)。

面向对象的开发原则中有一条:面向接口编程。

2、接口的声明格式

【修饰符】 interface 接口名{接口的成员列表;
}

3、类实现接口的格式

【修饰符】 class 实现类  implements 父接口们{}【修饰符】 class 实现类 extends 父类 implements 父接口们{}

4、接口继承接口的格式

【修饰符】 interface 接口名 extends 父接口们{接口的成员列表;
}

5、接口的特点

(1)接口不能直接实例化,即不能直接new对象

(2)只能创建接的实现类对象,那么接口与它的实现类对象之间可以构成多态引用。

(3)实现类在实现接口时,必须重写所有抽象的方法,否则这个实现类也得是抽象类。

(4)Java规定类与类之间,只能是单继承,但是Java的类与接口之间是多实现的关系,即一个类可以同时实现多个接口

(5)Java还支持接口与接口之间的多继承。

6、接口的成员

JDK1.8之前:

(1)全局的静态的常量:public static final,这些修饰符可以省略

(2)公共的抽象方法:public abstract,这些修饰符也可以省略

JDK1.8之后:

(3)公共的静态的方法:public static ,这个就不能省略了

接口中的静态方法

public interface Hello {static void sayHello(){System.out.println("Hello World!");}static void main(String[] args) {Hello.sayHello();}
}
interface TestInterface1 {static void sayHello(){System.out.println("TestInterface1 Hello");}
}
interface TestInterface2 extends TestInterface1 {}
public class Test implements TestInterface1,TestInterface2{public static void main(String[] args) {TestInterface2.sayHello();//编译报错}
}

这段代码在JDK8的环境下是能够编译通过并运行的。但是在接口中的静态方法有什么不同呢?

接口中的静态方法不能继承

interface TestInterface1 {static void sayHello(){System.out.println("TestInterface1 Hello");}
}
public class Test implements TestInterface1{public static void main(String[] args) {TestInterface1.sayHello();Test.sayHello();//编译报错}
}

原因:
因为一个类是可以实现多个接口的,如果接口中的静态方法的方法前面相同,就会发生继承冲突。所以索性就从继承这个层面阻断冲突的发生。反过来看由于接口中的字段是可以被继承的,所以实际上接口中的字段是存在继承冲突的。

interface TestInterface1 {String hello="TestInterface1";}
interface TestInterface1 {String hello="TestInterface2";
}
public class Test implements TestInterface1,TestInterface2{public static void main(String[] args) {System.out.println(Test.hello);//这里会报错}
}

总结
Java8中接口中静态字段可以被继承(默认用public static final修饰),静态方法不会继承,只能通过接口名调用。

(4)公共的默认的方法:public default,这个就不能省略了

7、default(默认)方法冲突问题

(1) 当一个实现类同时实现了两个或多个接口,这个多个接口的默认方法的签名相同。

解决方案:

方案一:选择保留其中一个

接口名.super.方法名(【实参列表】);

方案二:完全重写

(2)当一个实现类同时继承父类,又实现接口,父类中有一个方法与接口的默认方法签名相同

解决方案:

方案一:默认方案,保留父类的

方案二:选择保留接口的

接口名.super.方法名(【实参列表】);

方案三:完全重写

8、示例代码

public interface Flyable{long MAX_SPEED = 7900000;void fly();
}
public class Bird implements Flyable{public void fly(){//....}
}

9、常用的接口

(1)java.lang.Comparable接口:自然排序

​ 抽象方法:int compareTo(Object obj)

(2)java.util.Comparator接口:定制排序

​ 抽象方法:int compare(Object obj1 ,Object obj2)

(3)示例代码

如果员工类型,默认顺序,自然顺序是按照编号升序排列,那么就实现Comparable接口

class Employee implements Comparable{private int id;private String name;private double salary;//省略了构造器,get/set,toString@Overridepublic int compareTo(Object obj){return id - ((Employee)obj).id;}
}

如果在后面又发现有新的需求,想要按照薪资排序,那么只能选择用定制排序,实现Comparator接口

-1(<=0)表示不调换顺序,1(>0)表示需要调换

class SalaryComparator implements Comparator{public int compare(Object o1, Object o2){Employee e1 = (Employee)o1;Employee e2 = (Employee)o2;Salary() > e2.getSalary()){return 1;}else Salary() < e2.getSalary()){return -1;}return 0;}
}

7.10 内部类

1、内部类的概念

声明在另外一个类里面的类就是内部类。

2、内部类的4种形式

(1)静态内部类

(2)非静态成员内部类

(3)有名字的局部内部类

(4)匿名内部类

7.10.1 匿名内部类

1、语法格式:

//在匿名子类中调用父类的无参构造
new 父类(){内部类的成员列表
}//在匿名子类中调用父类的有参构造
new 父类(实参列表){内部类的成员列表
}//接口没有构造器,那么这里表示匿名子类调用自己的无参构造,调用默认父类Object的无参构造
new 父接口名(){}

2、匿名内部类、匿名对象的区别?

System.out.println(new Student("张三"));//匿名对象Student stu = new Student("张三");//这个对象有名字,stu//既有匿名内部类,又是一个匿名的对象
new Object(){public void test(){.....}
}.test();//这个匿名内部类的对象,使用obj这个名字引用它,既对象有名字,但是这个Object的子类没有名字
Object obj = new Object(){public void test(){.....}
};

3、使用的形式

(1)示例代码:继承式

abstract class Father{public abstract void test();
}
class Test{public static void main(String[] args){//用父类与匿名内部类的对象构成多态引用Father f = new Father(){public void test(){System.out.println("用匿名内部类继承了Father这个抽象类,重写了test抽象方法")}};f.test();}
}

(2)示例代码:实现式

interface Flyable{void fly();
}
class Test{public static void main(String[] args){//用父接口与匿名内部类的对象构成了多态引用Flyable f = new Flyable(){public void fly(){System.out.println("用匿名内部类实现了Flyable这个接口,重写了抽象方法");}};f.fly();}
}

(3)示例代码:用匿名内部类的匿名对象直接调用方法

new Object(){public void test(){System.out.println("用匿名内部类的匿名对象直接调用方法")}
}.test();

(4)示例代码:用匿名内部类的匿名对象直接作为实参

Student[] all = new Student[3];
all[0] = new Student("张三",23);
all[1] = new Student("李四",22);
all[2] = new Student("王五",20);//用匿名内部类的匿名对象直接作为实参
//这个匿名内部类实现了Comparator接口
//这个匿名内部类的对象,是定制比较器的对象
Arrays.sort(all, new Comparator(){public int compare(Obeject o1, Object o2){Student s1 = (Student)o1;Student s2 = (Student)o2;Age() - s2.getAge();}
});

7.10.2 静态内部类

1、语法格式

【修饰符】 class 外部类名  【extends 外部类的父类】 【implements 外部类的父接口们】{【其他修饰符】 static class  静态内部类 【extends 静态内部类自己的父类】 【implements 静态内部类的父接口们】{静态内部类的成员列表;}外部类的其他成员列表;
}

2、 使用注意事项

(1)包含成员是否有要求:

​ 可以包含类的所有成员

(2)修饰符要求:

  • ​ 权限修饰符:4种
  • ​ 其他修饰符:abstract、final

(3)使用外部类的成员上是否有要求

  • ​ 只能使用外部类的静态成员

(4)在外部类中使用静态内部类是否有要求

  • ​ 正常使用

(5)在外部类的外面使用静态内部类是否有要求

(1)如果使用的是静态内部类的静态成员外部类名.静态内部类名.静态成员
(2)如果使用的是静态内部类的非静态成员①先创建静态内部类的对象外部类名.静态内部类名 对象名 = new 外部类名.静态内部类名(【实参列表】);②通过对象调用非静态成员对象名.xxx

(6)字节码文件形式:外部类名$静态内部类名.class

3、示例代码

class Outer{private static int i = 10;static class Inner{public void method(){//...System.out.println(i);//可以}public static void test(){//...System.out.println(i);//可以}}public void outMethod(){Inner in = new Inner();in.method();}public static void outTest(){Inner in = new Inner();in.method();}
}
class Test{public static void main(String[] args){st();Outer.Inner in = new Outer.Inner();in.method();}
}

7.10.3 非静态内部类

1、语法格式

【修饰符】 class 外部类名  【extends 外部类的父类】 【implements 外部类的父接口们】{【修饰符】 class  非静态内部类 【extends 非静态内部类自己的父类】 【implements 非静态内部类的父接口们】{非静态内部类的成员列表;}外部类的其他成员列表;
}

2、 使用注意事项

(1)包含成员是否有要求:

​ 不允许出现静态的成员

(2)修饰符要求

​ 权限修饰符:4种

​ 其他修饰符:abstract,final

(3)使用外部类的成员上是否有要求

​ 都可以使用

(4)在外部类中使用非静态内部类是否有要求

​ 在外部类的静态成员中不能使用非静态内部类

(5)在外部类的外面使用非静态内部类是否有要求

//使用非静态内部类的非静态成员
//(1)创建外部类的对象
外部类名  对象名1 = new  外部类名(【实参列表】);//(2)通过外部类的对象去创建或获取非静态内部类的对象
//创建
外部类名.非静态内部类名  对象名2 = 对象名1.new 非静态内部类名(【实参列表】);//获取
外部类名.非静态内部类名  对象名2 = 对象名1.get非静态内部类对象的方法(【实参列表】);//(3)通过非静态内部类调用它的非静态成员
对象名2.xxx

(6)字节码文件形式:外部类名$非静态内部类名.class

3、示例代码

class Outer{private static int i = 10;private int j = 20;class Inner{public void method(){//...System.out.println(i);//可以System.out.println(j);//可以}}public void outMethod(){Inner in = new Inner();in.method();}public static void outTest(){// Inner in = new Inner();//不可以}public Inner getInner(){return new Inner();}
}
class Test{public static void main(String[] args){Outer out = new Outer();Outer.Inner in1 = w Inner();     //创建   	hod();Outer.Inner in2 = Inner();	//获取hod();}
}

7.10.4 局部内部类

1、语法格式

【修饰符】 class 外部类名  【extends 外部类的父类】 【implements 外部类的父接口们】{【修饰符】 返回值类型  方法名(【形参列表】){【修饰符】 class  局部内部类 【extends 局部内部类自己的父类】 【implements 局部内部类的父接口们】{局部内部类的成员列表;}}	外部类的其他成员列表;
}

2、 使用注意事项

(1)包含成员是否有要求

​ 不允许出现静态的成员

(2)修饰符要求

​ 权限修饰符:不能

​ 其他修饰符:abstract、final

(3)使用外部类的成员等上是否有要求

​ ①使用外部类的静态成员:随便用

​ ②使用外部类的非静态成员:能不能用要看所在的方法是否是静态的

​ ③使用所在方法的局部变量:必须 final修饰的

(4)在外部类中使用局部内部类是否有要求

​ 有作用域

(5)在外部类的外面使用局部内部类是否有要求

​ 没法使用

(6)字节码文件形式:外部类名$编号局部内部类名.class

3、示例代码

class Outer{private static int i = 10;private int j = 20;public void outMethod(){class Inner{public void method(){//...System.out.println(i);//可以System.out.println(j);//可以}}Inner in = new Inner();in.method();}public static void outTest(){final int k = 30;class Inner{public void method(){//...System.out.println(i);//可以System.out.println(j);//不可以System.out.println(k);//可以}}Inner in = new Inner();in.method();}
}

第八章 枚举与注解

8.1 枚举

1、枚举(JDK1.5引入的)

枚举类型的对象是有限、固定的几个常量对象。

2、语法格式

//形式一:枚举类型中只有常量对象列表
【修饰符】 enum 枚举类型名{常量对象列表
}//形式二:枚举类型中只有常量对象列表
【修饰符】 enum 枚举类型名{常量对象列表;其他成员列表;
}

说明:常量对象列表必须在枚举类型的首行

回忆:首行

(1)super()或super(实参列表):必须在子类构造器的首行

(2)this()或this(实参列表):必须在本类构造器的首行

(3)package 包; 声明包的语句必须在源文件.java的代码首行

(4)枚举常量对象列表必须在枚举类型的首行

3、在其他类中如何获取枚举的常量对象

//获取一个常量对象
枚举类型名.常量对象名//获取一个常量对象
枚举类型名.valueOf("常量对象名")//获取所有常量对象
枚举类型名[] all = 枚举类型名.values();

4、枚举类型的特点

(1)枚举类型有一个公共的基本的父类,是java.lang.Enum类型,所以不能再继承别的类型

(2)枚举类型的构造器必须是私有的

(3)枚举类型可以实现接口

interface MyRunnable{void run();
}
enum Gender implements MyRunnable{NAN,NV;public void run(){//...}
}
//或
enum Gender implements MyRunnable{NAN{public void run(){//...}},NV{public void run(){//...}};}

5、父类java.lang.Enum类型

(1)构造器

protected Enum(String name, int ordinal):由编译器自动调用

(2)String name():常量对象名

(3)int ordinal():返回常量对象的序号,第一个的序号是0

(4)String toString():返回常量对象名,如果子类想重写,需要手动

(5)int compareTo(Object obj):按照常量对象的顺序比较

8.2 注解

1、注解

它是代码级别的注释

2、标记符号:@

3、系统预定义的三个最基本的注解:

(1)@Override:表示某个方法是重写的方法

它只能用在方法上面,会让编译器对这个方法进行格式检查,是否满足重写的要求

(2)@SuppressWarnings(xx):抑制警告

(3)@Deprecated:表示xx已过时

4、和文档注释相关的注解

(1)文档注释

/**
文档注释
*/

(2)常见的文档注释

@author:作者

@since:从xx版本加入的

@see:另请参考

@param:形参

@return:返回值

@throws或@exception:异常

5、JUnit相关的几个注解

(1)@Test:表示它是一个单元测试方法

这个方法需要是:public void xxx(){}

(2)@Before:表示在每一个单元测试方法之前执行

这个方法需要是:public void xxx(){}

(3)@After:表示在每一个单元测试方法之后执行

这个方法需要是:public void xxx(){}

(4)@BeforeClass:表示在类初始化阶段执行,而且只执行一次

这个方法需要是:public static void xxx(){}

(3)@AfterClass:表示在类的“卸载”阶段执行,而且只执行一次

这个方法需要是:public static void xxx(){}

6、元注解

(1)@Target(xx):用它标记的注解能够用在xx位置

(xx):由ElementType枚举类型的10个常量对象指定,例如:TYPE,METHOD,FIELD等

例如:

@Target(ElementType.TYPE)@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
import static java.lang.annotation.ElementType.*;@Target({TYPE,METHOD,FIELD})

(2)@Retention(xx):用它标记的注解可以滞留到xx阶段

(xx):由RetentionPolicy枚举类型的3个常量对象指定,分别是:SOURCE,CLASS,RUNTIME

唯有RUNTIME阶段的注解才能被反射读取到

例如:

@Retention(RetentionPolicy.RUNTIME)

(3)@Documentd:用它标记的注解可以读取到API中

(4)@Inherited:用它标记的注解可以被子类继承

7、自定义注解

@元注解
【修饰符】 @interface 注解名{}@元注解
【修饰符】 @interface 注解名{配置参数列表
}

配置参数的语法格式:

数据类型  配置参数名();或数据类型  配置参数名() default 默认值;

关于配置参数:

(1)配置参数的类型有要求:

八种基本数据类型、String、枚举、Class类型、注解、它们的数组。

(2)如果自定义注解声明了配置参数,那么在使用这个注解时必须为配置参数赋值,除非它有默认值

@自定义注解名(配置参数名1=值,配置参数名2=值。。。)//如果配置参数类型是数组,那么赋值时,可以用{}表示数组
@自定义注解名(配置参数名1={值},配置参数名2=值。。。)

(3)如果配置参数只有一个,并且名称是value,那么赋值时可以省略value=

(4)如果读取这个注解时,要获取配置参数的值的话,可以当成方法一样来访问

自定义注解对象.配置参数();

例子

public class TestAnnotation {public static void main(String[] args) {//涉及到反射Class clazz=myclass.class;myAnotion my=(myAnotion) Annotation(myAnotion.class);String s=my.value();System.out.println(s);}
}
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)//只能有一个
@interface myAnotion{String value() default "a";
}
@myAnotion("bb")
class myclass{}

第九章 异常

9.1 异常的类型的体系结构

1、异常系列的超父类:java.lang.Throwable

(1)只有它或它子类的对象,才能被JVM或throw语句“抛”出

(2)也只有它或它子类的对象,才能被catch“捕获”

2、Throwable分为两大派别

(1)Error:严重的错误,需要停下来重新设计、升级解决这个问题

(2)Exception: 一般的异常,可以通过判断、检验进行避免,或者使用try…catch进行处理

3、Exception又分为两大类

(1)运行时异常:

​ 它是RuntimeException或它子类的对象。

​ 这种类型的异常,编译器不会提醒你,要进行throws或try…catch进行处理,但是运行时可能导致崩溃。

(2)编译时异常:

​ 异常除了运行时异常以外的都是编译时异常。

​ 这种类型的异常,编译器是强制要求你,throws或try…catch进行处理,否则编译不通过。

4、列出常见的异常类型

(1)运行时异常

RuntimeException、NullPointerException(空指针异常),ClassCastException(类型转换异常),ArithmeticException(算术异常),NubmerFormatException(数字格式化异常),IndexOutOfBoundsException(下标越界异常)(ArrayIndexOutOfBoundsException(数组下标越界异常)、StringIndexOutOfBoundsException(字符串下标越界异常))、InputMisMatchException(输入类型不匹配异常)。。。。

(2)编译时异常

FileNotFoundException(文件找不到异常)、IOException(输入输出异常)、SQLException(数据库sql语句执行异常)。。。

9.2 异常的处理

1、在当前方法中处理:try…catch…finally

//形式一&#atch
try{可能发生异常的代码
}catch(异常类型  异常名e){处理异常的代码(一般都是打印异常的信息的语句)
}catch(异常类型  异常名e){处理异常的代码(一般都是打印异常的信息的语句)
}。。。//形式二&#finally
try{可能发生异常的代码
}finally{无论try中是否有异常,也不管是不是有return,都要执行的部分
}//形式三&#atch..finally
try{可能发生异常的代码
}catch(异常类型  异常名e){处理异常的代码(一般都是打印异常的信息的语句)
}catch(异常类型  异常名e){处理异常的代码(一般都是打印异常的信息的语句)
}。。。
finally{无论try中是否有异常,也不管catch是否可以捕获异常,也不管try和catch中是不是有return,都要执行的部分
}

执行特点:

(1)如果try中的代码没有异常,那么try中的代码会正常执行,catch部分就不执行,finally中会执行

(2)如果try中的代码有异常,那么try中发生异常的代码的后面就不执行了,找对应的匹配的catch分支执行,finally中会执行

2、finally与return混合使用时

(1)如果finally中有return,一定从finally中的return返回。

此时try和catch中的return语句,执行了一半,执行了第一个动作。所以,finally中的return语句会覆盖刚刚的返回值

return 返回值; 语句有两个动作:(1)把返回值放到“操作数栈”中,等当前方法结束后,这个“操作数栈”中的值会返回给调用处(2)结束当前方法的执行

(2)如果finally中没有return,finally中的语句会执行,但是不影响最终的返回值

即try和catch中的return语句两步拆开来走,先把(1)把返回值放到“操作数栈”中,(2)然后走finally中的语句(3)再执行return后半个动作,结束当前方法

3、在当前方法中不处理异常,明确要抛给调用者处理,使用throws

语法格式:

【修饰符】 返回值类型  方法名(【形参列表】) throws 异常列表{}

此时调用者,就知道需要处理哪些异常。

方法的重写的要求:

(1)方法名:相同

(2)形参列表:相同

(3)返回值类型:

​ 基本数据类型和void:相同

​ 引用数据类型:<=

(4)修饰符:

​ 权限修饰符:>=

​ 其他修饰符:static,final,private不能被重写

(5)throws:<=

方法的重载:

(1)方法名:相同

(2)形参列表:必须不同

(3)返回值类型:无关

(4)修饰符:无关

(5)throws:无关

9.3 手动抛出异常:throw

throw 异常对象;//例如:
throw new AccountException("xxx");

throw抛出来的异常对象,和JVM抛出来的异常对象一样,也要用try…catch处理或者throws。

如果是运行时异常,编译器不会强制要求你处理,如果是编译时异常,那么编译器会强制要求你处理。

9.4 自定义异常

1、必须继承Throwable或它的子类

我们见到比较多的是继承RuntimeException和Exception.

如果你继承RuntimeException或它的子类,那么你自定义的这个异常就是运行时异常。编译器就不会提醒你处理。

如果你继承Exception,那么它属于编译时异常,编译器会强制你处理。

2、建议大家保留两个构造器

//无参构造
public 自定义异常名(){}//有参构造
public 自定义异常名(String message){super(message);
}

3、自定义异常对象,必须手动抛出,用throw抛出

9.5 关于异常的几个方法

(1)e.printStackTrace():打印异常对象的详细信息,包括异常类型,message,堆栈跟踪信息。这个对于调试,或者日志跟踪是非常有用的

(2&#Message():只是获取异常的message信息

关于异常信息的打印:

用打印和用e.printStackTrace()都是会标记红色的突出。

用System.out打印,当成普通信息打印。

这两个打印是两个独立的线程,顺序是不能精确控制的。

本文发布于:2024-01-28 16:41:02,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/17064312688804.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:部类   基础   注解   数组   语法
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23