-
JavaSE总结
Java语法基础
注释
在Java语言中,注释有以下三种方法:
- 单行注释:// 注释内容
- 多行注释:/* 注释内容 */
- 文档注释:/** 注释内容 */
示例:
/**
* (文档注释)
* @Description HelloWorld
* @Author 惟妙惟霄
*/
public class HelloWorld {
public static void main(String[] args) {
//(单行注释)这个程序的作用是输出Hello, world!
System.out.println("Hello, world!");
/*
(多行注释)
这个程序的作用是输出Hello, world!
*/
}
}
标识符
定义:Java所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。
注意:
- 所有的标识符都应该以字母(A-Z 或者 a-z),美元符($)、或者下划线(_)开始
- 首字符之后可以是字母(A-Z 或者 a-z),美元符($)、下划线(_)或数字的任何字符组合
- 关键字不能用作标识符
- 标识符是大小写敏感的
- 可以用中文命名,但是极不推荐
- 合法标识符举例:age、$salary、_value、__1_value
- 非法标识符举例:123abc、-salary
示例:
public class Demo01 {
public static void main(String[] args) {
String hello = "Hello, world!"; //小写字母开头,正确
String Hello = "Hello, world!"; //大写字母开头,正确
String $Hello = "Hello, world!"; //美元符开头,正确
String _Hello = "Hello, world!"; //下划线开头,正确
String Hello123 = "Hello, world!"; //非开头位置使用数字,正确
//String 123Hello = "Hello, world!"; //数字开头,错误
//String #Hello = "Hello, world!"; //井号开头,错误
//String *Hello = "Hello, world!"; //星号开头,错误
String 道德经 = "道可道,非常道。"; //使用中文,可以通过,但极不推荐
}
}
数据类型
Java是强类型语言。Java中的数据类型分为两种:
- 基本类型
- 引用类型
其中,基本数据类型分为以下四种:
- 整数型:byte(占1个字节)、short(占2个字节)、int(占4个字节)、long(占8个字节)
- 浮点型:float(占4个字节)、double(占8个字节)
- 字符型:char(占2个字节)
- 布尔型:boolean(占1位)
而引用数据类型分为以下三种:
- 类
- 接口
- 数组
示例:
public class Demo02 {
public static void main(String[] args) {
//八大基本数据类型
//整数
int num1 = 10; //最常用
byte num2 = 20;
short num3 = 30;
long num4 = 30L; //long类型在数字的最后加大写字母L
//浮点数
float num5 = 3.14159F; //float类型要在数字最后加F
double num6 = 3.14159;
//字符
char a = 'A';
//布尔值
boolean flag1 = true;
boolean flag2 = false;
}
}
其中,整数还可以使用不同的进制来表示:
- 二进制:0b开头
- 八进制:0开头
- 十进制
- 十六进制:0x开头
示例:
public class Demo03 {
public static void main(String[] args) {
int num1 = 10; //十进制
int num2 = 0b10; //二进制以0b开头
int num3 = 010; //八进制以0开头
int num4 = 0x10; //十六进制以0x开头
System.out.println(num1); //输出10
System.out.println(num2); //输出2
System.out.println(num3); //输出8
System.out.println(num4); //输出16
}
}
另外,浮点型因为精度和溢出的问题,尽量不要作为比较对象。
示例:
public class Demo03 {
public static void main(String[] args) {
float f1 = 2313467468515648F;
float f2 = f1 + 1;
System.out.println(f1==f2); //明明f2=f1+1,但仍然输出true,即f1与f2相等
}
}
而字符类型本质仍是整数。
示例:
public class Demo03 {
public static void main(String[] args) {
char c1 = 'a'; //赋值为a
System.out.println(c1); //输出a
System.out.println((int)c1); //输出97
char c2 = '中'; //赋值为中
System.out.println(c2); //输出中
System.out.println((int)c2); //输出20013
char c3 = 97; //直接用整数赋值
System.out.println(c3); //输出a
char c4 = '\u0061'; //转义为unicode编码
System.out.println(c4); //输出a
}
}
类型转换
由于Java是强类型语言,因而在进行某些整型、浮点型、字符型数据的混合运算中需要进行类型转换。不同类型的数据先转化为同一类型,然后进行运算。
低 ---------------------------------------> 高
byte/short/char —> int —> long—> float —> double
数据类型在进行转换时需注意以下几点:
- 不能对布尔型进行类型转换。
- 不能把对象类型转换成不相关的类型。
- 在把容量小的类型转换为容量大的类型时会进行自动类型转换。
- 在把容量大的类型转换为容量小的类型时必须使用强制类型转换。
- 转换过程中可能导致溢出或损失精度。
示例:
public class Demo04 {
public static void main(String[] args) {
//高->低 强制类型转换 (类型)变量名
int num1 = 128;
byte num2 = (byte)num1;
System.out.println(num2); //内存溢出,输出-128
//低->高 自动类型转换
double num3 = num1;
System.out.println(num3); //输出128.0
//精度丢失
float f = 3.5F;
double d = 3.5;
System.out.println((int)f); //输出3
System.out.println((int)d); //输出3
//计算中的类型转型,应先转型,再计算
int money = 10_0000_0000; //整数中可以任意使用下划线分割
int years = 20;
int total1 = money*years;
System.out.println(total1); //输出-1474836480,即结果溢出
long total2 = money*years;
System.out.println(total2); //输出-1474836480,即结果溢出
long total3 = money*((long)years);
System.out.println(total3); //输出20000000000,即结果正确
}
}
变量
在Java语言中,所有的变量在使用前必须声明。声明变量的基本格式如下:
type varname [ = value] [, varname[= value] ...];
//类型 变量名 = 值; 可以使用逗号隔开来同时声明多个同类型变量,但不推荐这么做
注意事项:
- 每个变量都有类型,可以是基本类型,也可以是引用类型。
- 变量名必须是合法的标识符。
- 变量声明是一条完整的语句,因此每一个声明都必须以分号结束。
- 变量名尽量遵守首字母小写的驼峰原则,例如:num、lastName、redApple、studentsNameList。
示例:
public class Demo05 {
public static void main(String[] args) {
int num = 123;
String name = "惟妙惟霄";
char x = 'X';
double pi = 3.14159;
}
}
Java语言支持的变量类型有:
- 类变量:独立于方法之外,且用 static 修饰的变量。
- 实例变量:独立于方法之外,不用 static 修饰的变量。
- 局部变量:位于方法之中的变量。
示例:
public class Demo06 {
//实例变量:从属于对象。可以不进行初始化,会赋默认值。
int a; //整数类型的默认值为0
boolean b; //布尔型的默认值为false
String c; //引用类型的默认值为null
//类变量:从属于类。使用static修饰。可以不进行初始化,会赋默认值。
static int d;
//main方法
public static void main(String[] args) {
//局部变量:必须声明且必须初始化值
int i = 10;
System.out.println(i);
//实例变量
Demo06 demo06 = new Demo06(); //需创建实例
System.out.println(demo06.a); //输出0
System.out.println(demo06.b); //输出false
System.out.println(demo06.c); //输出null
demo06.a = 10;
System.out.println(demo06.a); //输出10
demo06.b = true;
System.out.println(demo06.c); //输出true
demo06.c = "惟妙惟霄";
System.out.println(demo06.c); //输出惟妙惟霄
//类变量
System.out.println(d); //输出0
d = 12;
System.out.println(d); //输出12
}
}
常量
定义:常量是初始化后无法再次改动的值。常量名一般使用大写字符和下划线。其基本的声明格式如下:
final 类型 常量名 = 值;
//例如: final double PI = 3.14;
示例:
public class Demo07 {
//static与final都是修饰符,不存在先后顺序
static final double PI = 3.14;
public static void main(String[] args) {
System.out.println(PI); //输出3.14
}
}
运算符
Java语言支持以下运算符:
- 算术运算符:+、-、*、/、%、++、--
- 关系运算符:>、<、>=、<=、==、!=、instanceof
- 位运算符:&、|、^、~、>>、<<、>>>
- 逻辑运算符:&&、||、!
- 赋值运算符:=、+=、-=、*=、/=
- 条件运算符: ?:
- 其他运算符
算数运算符
示例:
package operator;
public class Demo01 {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 30;
int d = 40;
//二元运算符
System.out.println(a+b); //加法运算,输出30
System.out.println(b-c); //减法运算,输出-10
System.out.println(c*d); //乘法运算,输出1200
System.out.println(a/(double)d); //除法运算,输出0.25
System.out.println(d%c); //取余运算,输出10
//一元运算符
System.out.println(a); //输出10
System.out.println(a++); //输出10。当++在后时,先运算,后自增。
System.out.println(a); //输出11
System.out.println(++a); //输出12。当++在前时,先自增,后运算。
//利用Math类进行运算
double e = Math.pow(2, 3);
System.out.println(e); //输出8,即2的3次方
int f = Math.abs(-5);
System.out.println(f); //输出5,即-5的绝对值
}
}
关系运算符
示例:
package operator;
public class Demo02 {
public static void main(String[] args) {
//关系运算的返回结果是布尔类型,即结果只有true或false
int a = 10;
int b = 20;
System.out.println(a>b); //输出false
System.out.println(a<b); //输出true
System.out.println(a==b); //输出false
System.out.println(a!=b); //输出true
}
}
逻辑运算符
示例:
package operator;
public class Demo03 {
public static void main(String[] args) {
//逻辑运算符
//与(and) 或(or) 非(not)
boolean a = false;
boolean b = true;
System.out.println(a&&b); //输出false。逻辑与运算:两个都为真,结果才为真。
System.out.println(a||b); //输出true。逻辑或运算:两个有一个为真,结果就为真。
System.out.println(!a); //输出true。逻辑非运算:真变为假,假变为真。
//短路运算
int c = 5;
boolean d = (c<4)&&(c++<4);
System.out.println(d); //输出false
System.out.println(c); //输出5,说明c++<4并没有被执行,该语句被“短路”了
}
}
位运算符
示例:
package operator;
public class Demo04 {
public static void main(String[] args) {
//位运算
/*
* 设
* A = 1101 0001
* B = 0110 1000
* 则
* A&B = 0100 0000 按位与运算
* A|B = 1111 1001 按位或运算
* A^B = 1011 1001 按位异或运算,即不相等为1,相等为0
* ~B = 1001 0111 按位取反运算
* */
/*
题目:2*8如何运算才最快?
思路:2*8 = 2*2*2*2
0000 0000 0
0000 0001 1
0000 0010 2
0000 0011 3
0000 0100 4
0000 1000 8
0001 0000 16
<<为左移,每次左移等同于数值*2,但效率极高
>>为右移,每次右移等同于数值/2,但效率极高
*/
System.out.println(2<<3); //输出16
}
}
赋值运算符
示例:
package operator;
public class Demo05 {
public static void main(String[] args) {
int a = 10;
int b = 10;
a += b; //等同于a = a + b;
System.out.println(a); //输出20
a -= b; //等同于a = a - b;
System.out.println(a); //输出10
a *= b; //等同于a = a * b;
System.out.println(a); //输出100
a /= b; //等同于a = a / b;
System.out.println(a); //输出10
}
}
条件运算符
示例:
package operator;
public class Demo06 {
public static void main(String[] args) {
//条件运算符 a ? b : c
//如果a为true,则执行b,否则执行c
int score = 50;
String mark = score>=60 ? "及格" : "不及格";
System.out.println(mark); //输出"不及格"
}
}
Java流程控制
Scanner对象
在Java5及以后的版本中,我们可以通过java.util.Scanner来获取用户的输入。创建Scanner对象的基本语法如下:
Scanner sc = new Scanner(System.in);
我们可以通过 Scanner 类的 next() 与 nextLine() 方法获取输入的字符串。在读取前,我们一般需要使用 hasNext() 与 hasNextLine() 判断是否还有输入的数据。
使用next()时:
- 一定要读取到有效字符后才可以结束输入。
- 对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。
- 只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
- 不能得到带有空格的字符串。
示例:
package com.wmwx.Scanner;
import java.util.Scanner;
public class Demo01 {
public static void main(String[] args) {
//创建一个扫描器对象,用于从键盘接收数据
Scanner sc = new Scanner(System.in);
System.out.println("请输入内容:");
//判断用户有没有输入字符串
if (sc.hasNext()){
//使用next方式接收
String str = sc.next();
System.out.println("输入的内容为:"+str); //输入"hello world",输出"hello"
}
//凡是属于IO流的类如果不关闭就会一直占用资源
//要养成习惯,用完就关闭
sc.close();
}
}
使用nextLine()时:
- 以Enter为结束符,也就是说 nextLine()方法返回的是输入回车之前的所有字符。
- 可以获得带有空格的字符串。
示例:
package com.wmwx.Scanner;
import java.util.Scanner;
public class Demo02 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入内容:");
if (sc.hasNextLine()){
//使用nextLine方式接收
String str = sc.nextLine();
System.out.println("输入的内容为:"+str); //输入"hello world",输出"hello world"
}
sc.close();
}
}
顺序结构
Java的基本结构就是顺序结构,除非特别指明,否则就按照顺序从上到下一句一句执行。
示例:
package com.wmwx.struct;
public class Demo01 {
public static void main(String[] args) {
//顺序结构,从上到下依次执行
System.out.println("1");
System.out.println("2");
System.out.println("3");
System.out.println("4");
System.out.println("1");
}
}
选择结构
Java中的选择结构包含以下五种:
- if单选择结构
- if双选择结构
- if多选择结构
- 嵌套的if结构
- switch多选择结构
if单选择结构
语法如下:
if (布尔表达式){
//布尔表达式为true时执行的语句
}
示例:
package com.wmwx.struct;
import java.util.Scanner;
public class IfDemo01 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入内容:");
//判断是否输入了内容
if (scanner.hasNextLine()){
String str = scanner.nextLine();
//判断输入的字符串是否与"hello"相等
if (str.equals("hello")){
//如果相等,就输出这句话
System.out.println("你输入的是:"+str);
}
}
scanner.close();
}
}
if双选择结构
语法如下:
if (布尔表达式){
//布尔表达式为true时执行的语句
}
else {
//布尔表达式为false时执行的语句
}
示例:
package com.wmwx.struct;
import java.util.Scanner;
public class IfDemo02 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入成绩:");
int score = scanner.nextInt();
//判断考试分数不小于60就是及格,否则就是不及格
if (score>=60){
System.out.println("及格");
}else{
System.out.println("不及格");
}
scanner.close();
}
}
if多选择结构
语法如下:
if (布尔表达式1){
//布尔表达式1为true时执行的语句
}
else if (布尔表达式2){
//布尔表达式2为true时执行的语句
}
else if (布尔表达式3){
//布尔表达式3为true时执行的语句
}
//... ...
else if (布尔表达式n){
//布尔表达式n为true时执行的语句
}
else {
//以上布尔表达式都为false时执行的语句
}
注意:
-
if 语句至多有1 个else语句,else 语句在所有的else if 语句之后。
-
if语句可以有若干个else if语句,它们必须在else语句之前。
-
一旦其中一个else if语句检测为 true,其他的else if 以及else 语句都将跳过执行。
示例:
package com.wmwx.struct;
import java.util.Scanner;
public class IfDemo03 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入成绩:");
int score = scanner.nextInt();
//判断考试分数
if (score<100 && score>=95){
System.out.println("学分:4.5");
}else if (score<95 && score>=90){
System.out.println("学分:4.0");
}else if (score<90 && score>=85){
System.out.println("学分:3.5");
}else if (score<85 && score>=80){
System.out.println("学分:3.0");
}else if (score<80 && score>=75){
System.out.println("学分:2.5");
}else if (score<75 && score>=70){
System.out.println("学分:2.0");
}else if (score<70 && score>=65){
System.out.println("学分:1.5");
}else if (score<65 && score>=60){
System.out.println("学分:1.0");
}else if (score<60 && score>=0){
System.out.println("不及格");
}else{
System.out.println("成绩不合法!");
}
scanner.close();
}
}
嵌套的if结构
语法如下:
if (布尔表达式1){
//布尔表达式1为true时执行的语句
if (布尔表达式2){
//布尔表达式2为true时执行的语句
}
}
switch多选择结构
switch case 语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。
语法如下:
switch(expression){
case value1 :
//当expression与value1相等时执行的语句
break; //跳出switch结构
case value2 :
//当expression与value2相等时执行的语句
break;
//... ...
case valuen :
//当expression与valuen相等时执行的语句
break;
default :
//当expression与value1相等时执行的语句
}
示例:
package com.wmwx.struct;
public class SwitchDemo01 {
public static void main(String[] args) {
String grade = "B级";
switch (grade){
case "A级":
System.out.println("优秀!");
break;
case "B级":
System.out.println("良好!");
break;
case "C级":
System.out.println("中等!");
break;
case "D级":
System.out.println("及格!");
break;
case "E级":
System.out.println("挂科!");
break;
default:
System.out.println("未知的等级!");
}
}
}
循环结构
顺序结构的程序语句只能被执行一次。如果您想要同样的操作执行多次,,就需要使用循环结构。
Java中有以下四种循环结构:
- while 循环
- do…while 循环
- for 循环
- 针对数组的增强型 for 循环
while循环
while是最基本的循环,其语法为:
while( 布尔表达式 ) {
//循环内容
}
注意:
- 只要布尔表达式为 true,循环就会一直执行下去。
- 大多数情况下会让循环停止下来,需要一个让布尔表达式失效的方式来结束循环。
- 少部分情况下需要循环一直执行,比如服务器的请求响应监听等等。
- 布尔表达式如果恒为true就会造成死循环
示例:
package com.wmwx.struct;
public class WhileDemo02 {
public static void main(String[] args) {
//输出1~100并求它们的和
int i = 0;
int sum = 0;
while (i<100){
i++;
sum = sum + i;
System.out.println(i); //输出i
}
System.out.println(sum); //输出sum
}
}
do...while循环
do…while 循环和 while 循环相似,不同的是,do…while 循环至少会执行一次。而对于 while 语句而言,如果不满足条件,则不能进入循环。
其语法如下:
do {
//代码语句
}while(布尔表达式);
示例:
package com.wmwx.struct;
public class DoWhileDemo01 {
public static void main(String[] args) {
//输出1~100并求它们的和
int i = 0;
int sum = 0;
do {
i++;
sum = sum + i;
System.out.println(i); //输出i
}while (i<100);
System.out.println(sum); //输出sum
}
}
for循环
for循环执行的次数是在执行前就确定的,其语法格式如下:
for(初始化; 布尔表达式; 更新) {
//代码语句
}
关于 for 循环有以下几点说明:
- 最先执行初始化步骤。可以声明一种类型,但可初始化一个或多个循环控制变量,也可以是空语句。
- 然后,检测布尔表达式的值。如果为 true,循环体被执行。如果为false,循环终止,开始执行循环体后面的语句。
- 执行一次循环后,更新循环控制变量。
- 再次检测布尔表达式。循环执行上面的过程。
示例:
package com.wmwx.struct;
public class ForDemo01 {
public static void main(String[] args) {
//计算0~100以内的奇数和及偶数和
int odd = 0; //奇数
int even = 0; //偶数
for (int i = 1; i <= 100; i++) {
if(i%2==0){
//是偶数
even = even + i;
}else{
//是奇数
odd = odd + i;
}
}
System.out.println(odd); //输出2500
System.out.println(even); //输出2550
}
}
增强型for循环
Java5 引入了一种主要用于数组的增强型 for 循环。其语法格式如下:
for(声明语句 : 表达式)
{
//代码句子
}
声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。
表达式:表达式是要访问的数组名,或者是返回值为数组的方法。
示例:
package com.wmwx.struct;
public class ForDemo03 {
public static void main(String[] args) {
int[] numbers = {10, 20, 30, 40, 50}; //定义一个数组
//遍历数组中的元素
for (int x : numbers){
System.out.println(x);
}
}
}
Java方法详解
什么是方法?
Java方法是语句的集合,它们在一起执行一个功能。
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
示例:
package com.wmwx.method;
public class Demo01 {
//main方法
public static void main(String[] args) {
int sum = add(1, 2); //调用add方法
System.out.println(sum);
}
//add方法 实现加法功能
public static int add(int a, int b){
return a+b;
}
}
方法的优点
Java中的方法有以下几个优点:
- 使程序变得更简短而清晰。
- 有利于程序维护。
- 可以提高程序开发的效率。
- 提高了代码的重用性。
方法的设计原则
设计方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。我们设计方法的时候,最好保持方法的原子性,就是一个方法只完成1个功能,这样利于我们后期的扩展。
方法的定义
其基本语法为:
修饰符 返回值类型 方法名(参数类型 参数名){
...
方法体
...
return 返回值;
}
Java的方法包含一个方法头和一个方法体,类似于其他语言中的函数,是一段用来完成特定功能的代码片段。以下是一个方法的所有组成部分:
- 修饰符:可选,告诉编译器如何调用该方法,定义了该方法的访问类型。
- 返回值类型 :方法可能会有返回值,那么该返回值的数据类型就是该方法的返回值类型。有些方法执行所需的操作,但没有返回值。在这种情况下,方法的返回值类型使用关键字void。
- 方法名:是方法的实际名称。方法名和参数表共同构成方法签名。
- 参数类型:参数像是一个占位符,又被称为形参。当方法被调用时,传递值给参数,这个值被称为实参。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,一个方法可以不包含任何参数。
- 方法体:方法体包含具体的语句,定义该方法的功能。
例图:
方法的调用
Java 支持两种调用方法的方式,根据方法是否返回值来选择。
- 当方法返回一个值的时候,方法调用通常被当做一个值。
- 如果方法返回值是void,方法调用一定是一条语句。
示例:
package com.wmwx.method;
public class Demo02 {
public static void main(String[] args) {
int maxNum = max(10, 20); //调用有返回值的max方法,作为赋给变量maxNum的值
System.out.println(maxNum);
}
//定义有返回值的max方法
public static int max(int num1, int num2){
int result = 0;
if (num1==num2){
System.out.println("两数相等!");
return num1;
}
if (num1>num2){
result = num1;
}else{
result = num2;
}
return result;
}
}
方法的重载
重载:如果一个类的多个方法拥有相同的方法名,但是有不同的参数列表,那么Java编译器会根据方法签名判断哪个方法应该被调用。
方法重载的规则:
- 方法名称必须相同。
- 参数列表必须不同(如:个数不同、参数不同、参数排列顺序不同等)。
- 方法的返回类型可以相同,也可以不同
- 仅仅返回类型不同不足以成为方法的重载
示例:
package com.wmwx.method;
public class Demo03 {
public static void main(String[] args) {
int maxNum1 = max(10, 20); //调用有整数类型参数的max方法
System.out.println(maxNum1);
double maxNum2 = max(10.0, 20.0); //调用有浮点类型参数的max方法
System.out.println(maxNum2);
}
//定义有整数类型参数的max方法
public static int max(int num1, int num2){
int result = 0;
if (num1==num2){
System.out.println("两数相等!");
return num1;
}
if (num1>num2){
result = num1;
}else{
result = num2;
}
return result;
}
//重载:
//定义有浮点类型参数的max方法
public static double max(double num1, double num2){
double result = 0;
if (num1==num2){
System.out.println("两数相等!");
return num1;
}
if (num1>num2){
result = num1;
}else{
result = num2;
}
return result;
}
}
命令行传参
有的时候,希望一个程序在运行时再给它传递消息,这时就需要传递命令行参数给main方法进行实现。
示例:
package com.wmwx.method;
public class Demo04 {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println("args["+i+"]: "+args[i]);
}
}
}
可变参数/不定项参数
从Java5 开始,Java支持传递同类型的可变参数(又叫不定项参数)给一个方法。
方法的可变参数的声明如下所示:
typeName... parameterName
//即:在方法声明中,在指定参数类型后加一个省略号(...)
注意:
- 一个方法中只能指定一个可变参数。
- 可变参数必须是方法的最后一个参数。
- 任何普通的参数必须在可变参数之前声明。
示例:
package com.wmwx.method;
public class Demo05 {
public static void main(String[] args) {
//调用可变参数的方法
printMax();
printMax(12, 13.6, 22.5, 18.7, 16.0);
printMax(new double[]{12, 13.6, 22.5, 18.7, 16.0});
}
public static void printMax(double...numbers){
if (numbers.length == 0){
System.out.println("没有传入数据!");
return;
}
double result = numbers[0];
//找出最大值
for (int i=1;i<numbers.length;i++){
if (numbers[i]>result){
result = numbers[i];
}
}
System.out.println("最大的数是"+result+"!");
}
}
递归
定义:程序调用自身的编程方式称为递归。
利用递归可以用简单的程序来解决一些复杂的问题。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。
递归结构包括两部分:
- 递归头:什么时候不调用自身方法。如果没有递归头,将造成死循环。
- 递归体:什么时候调用自身方法。
示例:
package com.wmwx.method;
public class Demo06 {
public static void main(String[] args) {
System.out.println(factorial(5));
}
//求阶乘
public static int factorial(int n){
if (n==1){
return 1;
}else{
return n*factorial(n-1); //递归调用
}
}
}
Java数组详解
数组的定义
- 数组是相同数据类型的有序集合。
- 数组描述的是相同类型的若干数据,按照一定的先后次序排列组合而成。
- 其中,每一个数据称作一个数组元素,每一个数组元素可以通过下标来访问它们。
数组的声明和创建
首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的两种语法:
dataType[] arrayRefVar; // 首选的方法,推荐使用
或
dataType arrayRefVar[]; // 为C/C++程序员保留的方式,不推荐
Java语言使用new操作符来创建数组,语法如下:
arrayRefVar = new dataType[arraySize];
示例:
package com.wmwx.array;
public class Demo01 {
public static void main(String[] args) {
int[] numbers; //数组的声明
numbers = new int[5]; //数组的创建
}
}
数组的赋值
数组可以通过使用下标来执行赋值操作。数组的下标从0开始,到length-1结束(length为数组长度)。
如果下标超过length-1,就会报错:ArraylndexOutOfBoundsException ,即数组下标越界异常。
示例:
package com.wmwx.array;
public class Demo01 {
public static void main(String[] args) {
int[] numbers = new int[5];
numbers[0] = 1; //下标从0开始
numbers[1] = 2;
numbers[2] = 3;
numbers[3] = 4;
numbers[4] = 5;
System.out.println(numbers[0]); //输出1
System.out.println(numbers[1]); //输出2
System.out.println(numbers[2]); //输出3
System.out.println(numbers[3]); //输出4
System.out.println(numbers[4]); //输出5
System.out.println(numbers[5]); //报错:越界异常
}
}
数组的遍历
数组的元素类型和数组的大小都是确定的。所以当处理数组元素时候,我们通常使用循环结构。
示例:
package com.wmwx.array;
public class Demo01 {
public static void main(String[] args) {
int[] numbers = new int[5]; //声明和创建
numbers[0] = 1; //赋值
numbers[1] = 2;
numbers[2] = 3;
numbers[3] = 4;
numbers[4] = 5;
int sum = 0;
//通过array.length可以得到数组的长度
for (int i = 0; i < numbers.length; i++) {
sum = sum + numbers[i];
}
System.out.println(sum); //输出15
}
}
数组的初始化
数组的初始化有以下三种方式:
- 静态初始化
- 动态初始化
- 默认初始化
示例:
package com.wmwx.array;
public class Demo02 {
public static void main(String[] args) {
//静态初始化:创建+赋值
int[] a = {1, 2, 3, 4};
System.out.println(a[0]); //输出1
//动态初始化(包含默认初始化)
int[] b = new int[4];
b[0] = 5;
System.out.println(b[0]); //输出5
//默认初始化
System.out.println(b[1]); //输出0
System.out.println(b[2]); //输出0
System.out.println(b[3]); //输出0
}
}
数组的基本特点
- 其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。其元素必须是相同类型,不允许出现混合类型。
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型。
- 数组变量属于引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。
- 数组本身就是对象,而Java中的对象是在堆中的。因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。
For-Each循环
Java5 引进了一种新的循环类型,被称为 For-Each 循环或者加强型循环,它能在不使用下标的情况下遍历数组。
其语法格式如下:
for(type element: array)
{
System.out.println(element);
}
示例:
package com.wmwx.array;
public class Demo03 {
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5};
//输出数组的所有元素
//for循环
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
//for-each循环
for (int i: array) {
System.out.println(i);
}
}
}
数组作为参数和返回值
示例:
package com.wmwx.array;
public class Demo04 {
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5};
int[] new_array = reverse(array);
for (int i: new_array) {
System.out.println(i); //输出5、4、3、2、1
}
}
//数组作为返回值
public static int[] reverse(int[] array){ //数组作为参数
int[] result = new int[array.length];
//反转整个数组
for (int i=0, j=array.length-1;i<array.length;i++,j--){
result[j] = array[i];
}
return result;
}
}
多维数组
多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。
示例:
package com.wmwx.array;
public class Demo05 {
public static void main(String[] args) {
//二维数组
int[][] array = {{1, 2, 3}, {2, 3, 4}, {3, 4, 5}, {4, 5, 6}};
System.out.println(array[0][1]); //输出2
System.out.println(array[1][2]); //输出4
System.out.println(array[2][0]); //输出3
//遍历二维数组
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
System.out.println(array[i][j]);
}
}
}
}
Arrays类
java.util.Arrays 类能方便地操作数组,它提供的所有方法都是静态的。
具有以下功能:
- 打印数组:通过 toString 方法
- 给数组赋值:通过 fill 方法。
- 对数组排序:通过 sort 方法,按升序。
- 比较数组:通过 equals 方法比较数组中元素值是否相等。
- 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
示例:
package com.wmwx.array;
import java.util.Arrays;
public class Demo06 {
public static void main(String[] args) {
int[] array = {1, 234, 56, 7890};
//打印数组
System.out.println(array); //输出[I@e73f9ac
System.out.println(Arrays.toString(array)); //输出[1, 234, 56, 7890]
//排序数组
Arrays.sort(array); //升序排序
System.out.println(Arrays.toString(array)); //输出[1, 56, 234, 7890]
//查找元素
System.out.println(Arrays.binarySearch(array, 234)); //输出2
//填充数组
Arrays.fill(array, 0);
System.out.println(Arrays.toString(array)); //输出[0, 0, 0, 0]
//比较数组
System.out.println(Arrays.equals(array, new int[]{0, 0, 0, 0})); //输出true
}
}
稀疏数组
当一个数组中大部分元素为0,或为同一值时,可以使用稀疏数组来保存该数组。
稀疏数组的处理方式是:
- 记录数组一共有几行几列,有多少个不同的值
- 把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模
示例:
package com.wmwx.array;
import java.util.Arrays;
public class Demo07 {
public static void main(String[] args) {
//11*11的棋盘上:0 空格 1 黑棋 2 白棋
int[][] chess = new int[11][11];
chess[1][2] = 1;
chess[2][3] = 2;
//输出原始数组:
System.out.println("原始数组如下:");
for (int[] arrs: chess) {
for (int i: arrs) {
System.out.print(i+" ");
}
System.out.println();
}
System.out.println("=====================");
//转换为稀疏数组并输出
chess = sparse(chess);
System.out.println("稀疏数组如下:");
for (int[] arrs: chess) {
for (int i: arrs) {
System.out.print(i+" ");
}
System.out.println();
}
System.out.println("=====================");
//还原成原始数组并输出
chess = toArray(chess);
System.out.println("还原数组如下:");
for (int[] arrs: chess) {
for (int i: arrs) {
System.out.print(i+" ");
}
System.out.println();
}
}
public static int[][] sparse(int[][] array){
//第一步:获取有效值的个数
int sum = 0;
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
if (array[i][j]!=0){
sum++;
}
}
}
System.out.println("有效值的个数:"+sum);
System.out.println("=====================");
//第二步:创建一个稀疏数组
int[][] sparseArray = new int[sum+1][3];
sparseArray[0][0] = array.length;
sparseArray[0][1] = array[0].length;
sparseArray[0][2] = sum;
//第三步:遍历原数组,将非零值存放到稀疏数组中
int count = 0;
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
if (array[i][j]!=0){
count++;
sparseArray[count][0] = i;
sparseArray[count][1] = j;
sparseArray[count][2] = array[i][j];
}
}
}
//第四步:返回稀疏数组
return sparseArray;
}
public static int[][] toArray(int[][] sparse){
//第一步:构建原始数组
int[][] array = new int[sparse[0][0]][sparse[0][1]];
//第二步:还原其中的值
for (int i = 1; i < sparse.length; i++) {
array[sparse[i][0]][sparse[i][1]] = sparse[i][2];
}
//第三步:返回原始数组
return array;
}
}
最终的输出结果如下:
原始数组如下:
0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0
0 0 0 2 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
=====================
有效值的个数:2
=====================
稀疏数组如下:
11 11 2
1 2 1
2 3 2
=====================
还原数组如下:
0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0
0 0 0 2 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
Java面向对象
面向过程&面向对象
面向过程:
- 步骤清晰简单,第一步做什么,第二步做什么......
- 面向过程适合处理一些较为简单的事情
面向对象:
- 物以类聚,分类的思维模式。思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
- 面向对象适合处理复杂的问题,适合处理需要多人协作的问题。
概述:对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
什么是面向对象?
面向对象(Object-Oriented Programming, OOP)的本质是以类的方式组织代码,以对象的方式封装数据。
三大特性:
- 封装
- 继承
- 多态
注意:
- 从认识论的角度考虑,是先有对象后有类。因为对象是具体的,而类是抽象的。类是对对象的抽象。
- 从代码运行的角度考虑,是先有类后有对象。类是对象的模板。
类与对象的关系
类是一种抽象的数据类型。它是对某一类事物的整体描述或定义,但并不能代表某一个具体的事物。
- 如:人、动物、植物、电脑、手机,等等......
对象是抽象概念的具体实例。
- 如:张三、隔壁家的小花猫咪咪、《唐伯虎点秋香》里的那只名叫旺财的狗
初始化与创建对象
创建的方式:使用new关键字创建对象
使用new关键字创建对象的时候,除了分配内存空间之外,还会给创建好的对象赋默认值进行初始化,以及调用类中的构造器。
示例:
Student类
package com.wmwx.oop.Demo01;
//学生类
public class Student {
//属性:字段
String name;
int age;
//方法
public void study(){
System.out.println("学生"+this.name+"在学习。");
}
}
Application类(启动类)
package com.wmwx.oop.Demo01;
//启动类
public class Application {
//一个项目应该只存在一个main方法
public static void main(String[] args) {
//类是抽象的,需要实例化
//类实例化后会返回一个自己的对象
//student对象是Student类的一个实例
Student student = new Student();
student.study(); //输出"学生null在学习。"
student.name = "小明";
student.study(); //输出"学生小明在学习。"
}
}
构造方法
当一个对象被创建时候,构造方法用来初始化该对象。构造方法和它所在类的名字相同,但构造方法没有返回值。
一个类即使什么都不写,也会存在一个构造方法。因为 Java 自动提供了一个默认构造方法,其访问修饰符和类的访问修饰符相同。
一旦自己定义了构造方法,默认的构造方法就会失效。
示例:
Person类:
package com.wmwx.oop.Demo01;
public class Person {
String name;
//使用快捷键alt+insert可以自动生成构造方法
//无参构造
public Person(){
this.name = "一个无名氏";
}
//有参构造(一旦定义有参构造,就必须显式定义无参构造)
public Person(String name){
this.name = name;
}
}
Application类:
package com.wmwx.oop.Demo01;
public class Application {
public static void main(String[] args) {
//使用new关键字,本质是在调用构造方法
Person person1 = new Person(); //调用无参构造
System.out.println(person1.name); //输出"一个无名氏"
//利用构造方法,可以初始化对象
Person person2 = new Person("惟妙惟霄"); //调用有参构造
System.out.println(person2.name); //输出"惟妙惟霄"
}
}
内存分析
过程如下:
- 创建类
- 在堆中存放类和类中的静态方法
- 创建对象
- 在堆中为对象开辟空间
- 在栈中存放对象的引用变量名
- 令对象的引用变量名指向堆中开辟的空间
封装
所谓封装,即该露的露,该藏的藏。程序设计要追求“高内聚,低耦合”。
高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
低耦合:仅暴露少量的方法给外部使用。
对于代码而言,总结起来就一句话:属性私有,get/set。
意义:
- 提升程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口
- 提高了系统的可维护性
示例:
Student类:
package com.wmwx.oop.Demo03;
public class Student {
//属性私有
private String name;
private int id;
private String gender;
private int age;
//需要提供共有的get和set方法
//get方法:获取数据
public String getName() {
return name;
}
//set方法:设置数据
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age<0||age>120){
this.age = 3;
}else{
this.age = age;
}
}
}
Application类:
package com.wmwx.oop;
import com.wmwx.oop.Demo03.Student;
//启动类
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.setName("惟妙惟霄");
student.setId(123456);
student.setGender("男");
student.setAge(130);
System.out.println(student.getAge()); //输出3
}
}
继承
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
在 Java 中通过 extends 关键字可以声明一个类是从另外一个类继承而来的,其语法如下:
class 子类 extends 父类 {
}
extends的意思是扩展,子类是对父类的扩展。
继承的注意事项:
-
Java只有单继承,没有多继承。
-
在Java中,所有类都默认直接或间接继承自Object类。
在Java中,可以使用this指代当前类,并使用super指代父类。
super的注意事项:
- super调用父类的构造方法,必须在构造方法的第一行。
- super只能出现在子类的方法或构造方法中。
- super和this不能同时调用构造方法。
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说,子类能够根据需要实现父类的方法。重写需要遵守以下规则:
- 方法名必须相同
- 参数列表必须相同
- 修饰符的范围可以扩大,但不能缩小
- 抛出的异常可以被缩小,但不能扩大
示例:
Person类:
package com.wmwx.oop.Demo04;
//父类:人类
public class Person {
private int money = 10_0000_0000;
protected String name = "惟妙惟霄";
public Person() {
System.out.println("Person的无参构造方法执行了!");
}
public void say(){
System.out.println("说了一句话。");
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
public void print(){
System.out.println("Person");
}
}
Student类:
package com.wmwx.oop.Demo04;
//子类:学生类
public class Student extends Person{
private String name = "妙霄";
public Student() {
//隐藏代码:super();
//父类的构造方法必须要在子类的构造方法的第一行
System.out.println("Student的无参构造方法执行了!");
}
public void test1(String name){
System.out.println(name); //输出参数
System.out.println(this.name); //输出当前类的name
System.out.println(super.name); //输出父类的name
}
//重写都是方法的重写,与属性无关
//只允许重写public方法
//可以使用快捷键alt+insert来插入重写方法
@Override
public void print() {
System.out.println("Student");
}
public void test2(){
print();
this.print();
super.print();
}
}
Application类:
package com.wmwx.oop;
import com.wmwx.oop.Demo04.Person;
import com.wmwx.oop.Demo04.Student;
//启动类
public class Application {
public static void main(String[] args) {
Student student = new Student();
//第一行输出"Person的无参构造方法执行了!"
//第二行输出"Student的无参构造方法执行了!"
student.say(); //子类继承父类,就会拥有父类的public方法
System.out.println(student.getMoney()); //可以用父类的get/set方法对属性进行操作
//可以使用快捷键ctrl+H来查看继承树
student.test1("MiaoXiao");
//第一行输出"MiaoXiao"
//第二行输出"妙霄"
//第三行输出"惟妙惟霄"
student.test2();
//第一行输出"Student"
//第二行输出"Student"
//第三行输出"Person"
Student stu1 = new Student();
stu1.print(); //输出"Student"
//父类的引用指向了子类
Person stu2 = new Student();
stu2.print(); //输出"Student"
}
}
多态
多态是同一个行为具有多个不同表现形式或形态的能力。当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态存在的三个必要条件:
- 继承
- 重写
-
父类引用指向子类对象:
Parent p = new Child();
不能被重写的方法:
- static 方法
- final 方法
- private 方法
示例:
Person类:
package com.wmwx.oop.Demo05;
public class Person {
public void run(){
System.out.println("人在跑步。");
}
}
Student类:
package com.wmwx.oop.Demo05;
public class Student extends Person{
@Override
public void run() {
System.out.println("学生在跑步。");
}
public void eat(){
System.out.println("学生在吃东西。");
}
}
Application类:
package com.wmwx.oop;
import com.wmwx.oop.Demo05.Person;
import com.wmwx.oop.Demo05.Student;
//启动类
public class Application {
public static void main(String[] args) {
//一个对象的实际类型是确定的
//但可以指向的引用类型是不确定的
Student s1 = new Student();
Person s2 = new Student(); //父类的引用指向子类
Object s3 = new Student();
s1.run(); //输出"学生在跑步"
s2.run(); //子类重写父类方法,将执行子类方法,输出"学生在跑步。"
s1.eat(); //输出"学生在吃东西"
//s2.eat(); //不能调用。对象能使用哪些方法,要看左边的类型。
((Student)s2).eat(); //强制类型转换。输出"学生在吃东西。"
}
}
instanceof
Java中的instanceof关键字可以用来判断某一个对象是不是某一个类是实例。如果是,返回true;如果不是,返回false;如果二者无关,则编译不通过。
示例:
package com.wmwx.oop;
import com.wmwx.oop.Demo06.Person;
import com.wmwx.oop.Demo06.Student;
import com.wmwx.oop.Demo06.Teacher;
//启动类
public class Application {
public static void main(String[] args) {
//继承关系如下:
//Object -> Person -> Student
//Object -> Person -> Teacher
//Object -> String
Object object = new Student();
System.out.println(object instanceof Student); //输出true
System.out.println(object instanceof Person); //输出true
System.out.println(object instanceof Object); //输出true
System.out.println(object instanceof Teacher); //输出false
System.out.println(object instanceof String); //输出false
System.out.println("=====");
Person person = new Student();
System.out.println(person instanceof Student); //输出true
System.out.println(person instanceof Person); //输出true
System.out.println(person instanceof Object); //输出true
System.out.println(person instanceof Teacher); //输出false
//System.out.println(person instanceof String); //编译时报错
System.out.println("=====");
Student student = new Student();
System.out.println(student instanceof Student); //输出true
System.out.println(student instanceof Person); //输出true
System.out.println(student instanceof Object); //输出true
//System.out.println(student instanceof Teacher);//编译时报错
//System.out.println(student instanceof String); //编译时报错
}
}
类型转换
- 子类转换为父类,是向上转型,可自动转换。
- 父类转换为子类,是向下转型,需强制转换。
示例:
Person类:
package com.wmwx.oop.Demo06;
public class Person {
public void run(){
System.out.println("人在跑步。");
}
}
Student类:
package com.wmwx.oop.Demo06;
public class Student extends Person{
public void walk(){
System.out.println("学生在走路。");
}
}
Application类:
package com.wmwx.oop;
import com.wmwx.oop.Demo06.Person;
import com.wmwx.oop.Demo06.Student;
//启动类
public class Application {
public static void main(String[] args) {
//高 ----------------- 低
Person person = new Student();
((Student)person).walk(); //强制类型转换
Student student = new Student();
Person obj = student; //子类转换为父类,可能会丢失一些方法
//obj.walk(); //编译时报错
}
}
静态代码块
静态代码块会在类加载时执行,且只会执行一次。
示例:
Person类:
package com.wmwx.oop.Demo07;
public class Person {
//第二个执行,可在这里赋初始值
{
System.out.println("匿名代码块");
}
//第一个执行,只执行一次
static {
System.out.println("静态代码块");
}
//第三个执行
public Person() {
System.out.println("构造方法");
}
}
Application类:
package com.wmwx.oop;
import com.wmwx.oop.Demo07.Person;
//启动类
public class Application {
public static void main(String[] args) {
Person person = new Person();
//第一行输出"静态代码块"
//第二行输出"匿名代码块"
//第三行输出"构造方法"
}
}
抽象类
在Java语言中使用abstract 来定义抽象类,其基本语法如下:
abstract class 类名{
//属性
//方法
}
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。在Java中一个类只能继承一个抽象类,但一个类可以实现多个接口。
在Java语言中使用abstract 来定义抽象方法,其基本语法如下:
abstract 访问修饰符 返回值类型 方法名(参数);
抽象类与抽象方法的规则:
- 抽象类不能被实例化(即不能被 new ),只有抽象类的非抽象子类可以创建对象。
- 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
- 抽象类中的抽象方法只是声明,不包含方法体,也就是不给出方法的具体实现。
- 构造方法、类方法(用 static 修饰的方法)不能声明为抽象方法。
- 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
示例:
Action类:
package com.wmwx.oop.Demo08;
//使用abstract声明抽象类
public abstract class Action {
//抽象方法,只有方法名,没有方法体
//仅作约束,期待他人实现
public abstract void doSomething();
}
A类:
package com.wmwx.oop.Demo08;
public class A extends Action{
//子类必须实现父类的抽象方法
//除非该子类是抽象类
@Override
public void doSomething() {
}
}
接口
接口在JAVA编程语言中是一个抽象类型,是抽象方法的集合,通常以 interface 来声明。其基本语法如下:
[访问修饰符] interface 接口名称 [extends 其他的接口名称] {
// 抽象方法
}
接口的特性:
- 接口中每一个方法都是隐式抽象的,接口中的方法会被隐式地指定为 public abstract,并且只能是 public abstract。
- 接口中可以含有变量,但是接口中的变量会被隐式地指定为 public static final ,并且只能是 public static final。
- 接口中的方法不能在接口中实现,只能由实现接口的类来实现接口中的方法。
- 一个接口能继承另一个接口,使用 extends 关键字.子接口会继承父接口的方法。
当类实现接口的时候,需要实现接口中所有的方法。否则,类必须声明为抽象的类。Java中使用 implements关键字实现接口,其基本语法如下:
class 类名 implements 方法名{
//实现接口中的抽象方法
}
示例:
UserService接口:
package com.wmwx.oop.Demo09;
//定义接口使用关键字interface
public interface UserService {
//接口中的所有属性都是public static final
//一般不在接口中定义属性
int age = 60;
//接口中的所有方法都是public abstract
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
TimeService接口:
package com.wmwx.oop.Demo09;
public interface TimeService {
void timer();
}
UserServiceImpl类:
package com.wmwx.oop.Demo09;
//类使用关键字implements来实现接口
//实现了接口的类,需要重写接口的所有方法
//一个类可以实现多个接口
public class UserServiceImpl implements UserService, TimeService{
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
@Override
public void timer() {
}
}
内部类
所谓内部类,就是在一个类的内部再定义一个类。
示例:
Outer类:
package com.wmwx.oop.Demo10;
//外部类
public class Outer {
private int id=10;
public void out(){
System.out.println("这是外部类的方法!");
}
public class Inner{
public void in(){
System.out.println("这是内部类的方法!");
}
//获得外部类的私有属性
public int getId(){
return id;
}
}
public void method(){
//局部内部类,在外部类的方法之中
class Inner{
}
}
}
//一个java文件可以有多个class,但是只能有一个public class
class A{
}
Application类:
package com.wmwx.oop;
import com.wmwx.oop.Demo09.UserService;
import com.wmwx.oop.Demo10.Outer;
//启动类
public class Application {
public static void main(String[] args) {
//外部类使用new关键字
Outer outer = new Outer();
outer.out();
//内部类通过外部类来实例化
Outer.Inner inner = outer.new Inner();
inner.in();
System.out.println(inner.getId());
//匿名类,不用将实例保存到变量中
new Outer().out();
//使用匿名类实现接口
UserService userService = new UserService() {
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
}
}
}
Java异常机制
异常
异常是程序中的一些错误。但并不是所有的错误都是异常,并且错误有时候是可以避免的。
异常的分类:
- 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
- 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- 错误(Error): 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,在编译时同样检查不到。
示例:
package com.wmwx.exception;
public class Demo01 {
public static void main(String[] args) {
new Demo01().a(); //抛出异常:StackOverflowError
System.out.println(11/0); //抛出异常:ArithmeticException
System.out.println() //抛出异常:Error
}
public void a(){
b();
}
public void b(){
a();
}
}
异常体系结构
Java把异常当做对象来处理,并定义了一个基类java.lang.Throwable作为所有异常的超类。这些异常分为两大类,即异常(Exception)和错误(Error)。其中,Exception类有两个主要的子类,即 IOException 类和 RuntimeException 类。
Error
Error类对象由虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。
常见的错误如Java虚拟机运行错误(VirtualMachineError),当JVM不再有继续执行操作所需的内存资源时,将出现OutOfMemoryError。这些异常发生时,Java虚拟机一般会选择将线程中止。
还有发生在虚拟机试图执行应用时的错误,如类定义错误(NoClassDefFoundError)、链接错误(LinkageError)等,这些错误是不可查的。因为它们在应用程序的控制和处理程序能力之外,而且绝大多数是程序运行时不允许出现的状况。
Exception
在Exception类的分支中有一个重要的子类 RuntimeException(运行时异常),其中包含以下异常:
-
ArrayIndexOutOfBoundsException(数组下标越界)
-
NullPointerException(空指针异常)
-
ArithmeticException(算术异常)
-
MissingResourceException(丢失资源)
-
ClassNotFoundException(找不到类)
这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
Error和Exception的区别:
- Error 通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程
- Exception 通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。
捕获异常
使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。
try/catch代码块中的代码称为保护代码,其基本语法如下:
try
{
// 程序代码
}catch(ExceptionName e1)
{
// Catch 块
}catch(ExceptionName e2) //e2需比e1范围更大
{
// Catch 块
}finally {
// 终究会执行
}
示例:
package com.wmwx.exception;
public class Demo02 {
public static void main(String[] args) {
int a = 11;
int b = 0;
//快捷键 ctrl+alt+T
try{ //try 监控区域
System.out.println(a/b);
}catch (ArithmeticException e){ //catch 捕获异常(参数是异常类型)
System.out.println("程序出现异常,变量b不能为0!");
}finally { //finally 善后工作
System.out.println("finally终究会被执行。");
}
try{ //监控区域
new Demo02().a();
}catch (StackOverflowError e){ //捕获异常,多个类别需要从小到大
System.out.println("StackOverflowError"); //会输出
}catch (Exception e){
System.out.println("Exception"); //不会输出
}catch (Throwable t){
System.out.println("Throwable"); //不会输出
}finally { //善后工作
System.out.println("finally终究会被执行。");
}
}
public void a(){
b();
}
public void b(){
a();
}
}
抛出异常
如果一个方法没有捕获到一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。
也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
注意:两种方式的拼写不同,第一种末尾有 s,第二种没有。
示例:
package com.wmwx.exception;
public class Demo03 {
public static void main(String[] args) {
int a = 11;
int b = 0;
try {
new Demo03().devide(a, b);
} catch (ArithmeticException e) {
e.printStackTrace();
} finally {
}
}
//假设在方法中无法处理异常,可以用throws主动抛出异常
public void devide(int a, int b) throws ArithmeticException{
if (b==0){
throw new ArithmeticException(); //使用throw主动抛出异常,一般用在方法中
}
System.out.println(a / b);
}
}
自定义异常
使用Java内置的异常类可以描述在编程时出现的大部分异常精况。除此之外,用户还可以自定义异常,只需继承Exception类即可。
示例:
MyException类(自定义异常类):
package com.wmwx.exception;
//自定义的异常类
public class MyException extends Exception{
//假设:当传递过来的数字大于10时抛出异常
private int detail;
//构造方法
public MyException(int a) {
this.detail = a;
}
//toString 打印异常信息
@Override
public String toString() {
return "MyException{detail=" + detail + "}";
}
}
Demo04类(测试类):
package com.wmwx.exception;
//自定义异常的测试类
public class Demo04 {
//可能会存在异常的方法
public void test(int a) throws MyException {
System.out.println("传递的参数为:"+a);
if (a>10){
throw new MyException(a); //抛出异常
}
System.out.println("没有异常!");
}
public static void main(String[] args) {
try {
new Demo04().test(5); //没有异常
new Demo04().test(15); //有异常
} catch (MyException e) {
System.out.println("MyException => " + e);
}
}
}
经验总结
- 处理运行时异常时,采用逻辑去合理规避,同时辅助 try-catch 处理。
- 在多重catch块后面,可以加一个 catch (Exception) 来处理可能会被遗漏的异常。对于不确定的代码,也可以加上try-catch,处理潜在的异常。
- 尽量去处理异常,切忌只是简单地调用 printStackTrace() 去打印输出。具体如何处理异常,要根据不同的业务需求和异常类型去决定。
- 尽量添加 finally 语句块去释放占用的资源。
原文:https://www.cnblogs.com/wmwx/p/14491874.html