Java基础
基本数据类型
| 字节类型 | 字节范围 | ||
| 整数类型 | byte(百位) | 1 | -128-127 |
| short(万位) | 2 | ||
| int(21亿左右) | 4 | - 2147483648-2147483647 | |
| long(近乎无穷了) | 8 | -9223272036854775808-9223272036854775807 | |
| 浮点型 | float | 3.4e-45~1.4e38 | |
| double | 4.9e-324~1.8e308 | ||
| 字符型 | char | 2 | |
| boolean | 1 | true 和 false |
什么是字节?
-
位(bit):是计算机内部数据存储的最小单位,11001100是一个八位二进制数
-
字节(byte):计算机中数据处理的基本单位,习惯用B来表示
电脑32位和64位的区别?
-
寻址能力不同
默认初始化都为多少?
-
char: u0000
-
boolean:false
-
基本数据类型扩展
整数拓展
八进制和十六进制可以通过int直接输出
int i1 = 10;
int i2 = 010;//八进制:0-7
int i3 = 0x10;//十六进制:0-9 A-F
i2 = 8;
i3 = 17;
浮点数拓展
在银行业务中。浮点数不能使用 double 和 float 使用的Decimal
字符拓展
字符本质上还是数字,使用的是Unicode表
转义字符
- \t 制表符
- \n 换行
char c1 = 'A';
char c2 = '中';
System.out.println(c1);
(输出:A)
System.out.println((int)c1);
(输出:65)
System.out.println(c2);
(输出:中)
System.out.println((int)c2);
(输出:20013)
char c3 = '\u0061';
System.out.println(c3);
(输出:a)
布尔值拓展
boolean flag = true;
if(flag==true){(新手:但是可读性更高)}
if(flag){老手:增加后期的读取难度,但是代码会更加简洁}
类型转换
//类型转换案列
int a = 128;
byte b = (byte)a;
//强制转换, (类型)变量名 高--低 需要手动强制转换。 低--高。不需要,自动转换
System.out.println(a);
System.out.println(b);//byte的精度是-128-127 ,产生了内存溢出
/*
注意点:
1、不能对布尔值进行转换
2、不能把对象类型转换成不相干的数据类型
3、在把高--低的时候,需要进行强制转转
4、转换的时候,可能出现内存溢出,注意精度问题
*/
//转换时注意精度问题,防止精度丢失
System.out.println("================================");
System.out.println((int)23.7);
System.out.println((int)23.22222f);
//低--高,自动转换
System.out.println("================================");
char c = 'a';
int i = c + 1;
System.out.println(i);
System.out.println((char)i);
注意事项:
操作较大数的时候,注意溢出的问题
JDK7的新特性,数字之间可以用下划线分割,方便查看
public static void main(String[] args) {
int money = 10_0000_0000;
int year = 20;
//两种均是错误写法
//int total = year*money;
//year*money在赋值前已经是int类型,再给到long total的值也是错误的
//long total = year*money;
//使用强制转换后,可以可以得到正确的值
long total = year*((long)money);
System.out.println(total);
}
变量
变量是什么?
变量就是可以变化的量
变量是程序中最基本的存储单元,包括变量名,变量类型和作用域;
变量写法:type varName [=value] [{,varName}];//数据类型 变量名 = 值;可以使用逗号隔开来声明多个同类型的变量;
Java是一种强类型语言,所以每个变量必须声明其类型;
注意事项:
变量名必须是合法的标识符
变量声明是一条完整的语句,因此每一个声明都必须以分号结束
虽然可以定义多个变量,但是不建议这样写!
变量作用域
- 类变量
- 局部变量
//类变量
static int allClicks = 0;
//实例变量
String str = "Hello World";
//局部变量
public void method(){
int i = 0;
}
常量
常量(Constant):初始化(initialize)后不能再改变的值!不会变动的值
所谓常量可以理解成一种特使的变量,特的值被设定后,在程序运行过程中不运行被改变
final 常量值 = 值;
final double = PI = 3.14;
常量名一般使用大写的字符
变量的命名规范
- 所有变量、方法、类名:见知其意
- 类成员变量:首字母小写和驼峰原则: monthSalary
- 局部变量:首字母小写和驼峰原则
- 类名:首字母大写和驼峰原则:Man
- 方法名:首字母小写和驼峰原则:runRun();
运算符
- 算数运算符:+,-*%,++,--
- 赋值运算符 :=
- 关系运算符 :>,<.>=,<=,!=,==,instanceof
- 逻辑运算符:&&,||,!
- 位运算符:&,|,%^,~,>>,<<,>>>
- 条件运算符:? :
- 扩展赋值运算符:+=,-=,*=,/=
包机制
Java提供包机制,用于区别类名的命名空间
为了能够使用某一个包的成员,我们需要在Java程序中明确导入该包。使用"import"语句可以完成此功能。
Java Doc
/**
* @anthor muzhi 作者
* @version 1.0 版本号
* @since 1.8 指明需要使用最早使用的jdk版本
* @param 参数名
* @return 返回值情况
* @throws 异常抛出情况
*
*/
导出操作:
在工具栏中找到 tool >> Generate JAVADoc…

然后弹出配置窗口

配置窗口各个参数说明
1. Whole project>>整个项目都生成

3. include test source 包含测试目录
4. include JDK and … 包含jdk和其他的第三方jar
5. link to JDK documentation…链接到JDK api
6. output directy 生成的文档存放的位置
7. private、package、protected、public 生成文档的级别(类和方法)
8. 右边的Generate…是选择生成的文档包含的内容,层级树、导航、索引..
9. 再右边是生成的文档包含的内容信息,作者版本等信息
10. Locale 语言类型,zh-CN
11. Other command line arguments 其他参数
12. Maximum heep… 最大堆栈
Java流程控制
用户与Scanner
Java 提供了一个工具类,用于实现程序与人的交互(JDK5新特性)
Scanner类(用于获取用户的输入)
Scanner scanner = new Scanner(System.in)
Scanner类是通过
- next()与nextLine()方法获取输入的字符串
next() [注意事项]:
- 一定要读取到有效字符后才可以结束输入
- 对输入有效字符之前遇到的空白,next()方法会自动去除
- 只有输入有效字符后才将其后面输入的空白作为分隔符和结束符
- next()不能得到带有空格的字符串
nextLine()[注意事项]:
- 以Enter为结束符,也就是说nextLine()方法返回的是输入回车之前的所有字符
- 可以获得空白
顺序结构
Java的基本结构就是顺序结构,除非特别指明,否则就是一句一句执行
顺序结构为最简单的算法结果
选择结构
if单选择结构
if(布尔表达式){
//如果布尔表达为true为真就会通过该语句
}
if双选择结构
if(布尔表达式){
//如果布尔表达式值为true
}else{
//如果布尔表达式值为false
}
if多选择结构
if(布尔表达式){
//如果布尔表达式值为true
}else if(布尔表达式 1){
//如果布尔表达式值为true
}else if(布尔表达式 1){
//如果布尔表达式值为true
}
嵌套的if结构
if(布尔表达式){
//如果布尔表达式值为true
if(布尔表达式){
//如果布尔表达式值为true
}
}
switch多选择结构
switch(expression){
case value :
//语句
break;可选
case value;
break;
default;
}
switch语句中变量类型可以是byte、short、int或者char、从JDK 7开始、switch支持字符串String类型了、同时case标签必须为字符串常量或字面量
循环结构
while循环
while(布尔表达式){
//循环内容
}
-
有时候我们需要即使不满足条件,也至少执行一次
do{
//代码语句
}while{布尔表达式);
-
while是先判断后执行,do...while是先执行后判断 !
-
do...while总是保证循环体会被至少执行一次!这是他们主要的差别(就好比网站输入密码)
-
for循环是支持迭代的一种通用结构,是最有效,最灵活的循环结构。
-
for(初始化;布尔表达式;更新){
//代码语句
}
增强型for循(JDK5新特性
for(声明语句:表达式){
//代码句子
}
1、声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块内,其值与此时数组元素的值相等、
2、表达式:表达式是要访问的数组名,或者是返回值为数组的方法。
练习题:九九乘法表
package com.li.struct;
public class ForDemo04 {
public static void main(String[] args) {
//九九乘法表
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(i+"*"+j+"="+i*j+"\t");
}
System.out.println();
}
}
}
break continue
break: 在任何循环语句的主体部分,均可用break控制循环的流程。break用于强行退出循环,不执行循环中剩余的语句、(break语句在switch语句中也使用)
continue:在循环语句体重,用于终止某次多的循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。
练习题:打印三角形
package com.li.struct;
//打印三角形
public class TestDemo {
public static void main(String[] args) {
//外层循环:三角形的高
for (int i = 1;i<=4;i++){
//半个倒三角形,构成是空格
for (int j = 3;j>=i;j--){
System.out.print(" ");
}
//右边半个倒三角形,构成是星星
for (int j = 1;j<=i;j++){
System.out.print("*");
}
//右边的右边半个倒三角形,构成是星星,j<i代表第一行不打印从第二行开始
for (int j = 1;j<i;j++){
System.out.print("*");
}
System.out.println();
}
}
}

关于goto关键字
goto关键字很早在程序设计语言中出现。尽管goto仍是Java的一个保留字,但并未在语言中得到正式使用;Java没有goto。然而,在break和continue这两个关键字的身上,我们仍然能看出一些goto的影子---带标签的break和continue。
“标签”是指后面跟一个冒号的标识符,例如:label
Java方法
什么是方法?
-
Java方法是解决一类问题的步骤的有序集合
-
方法包含于类或对象中
-
方法的命名规则
-
符合驼峰原则
方法的定义与调用
语法定义:
修饰符 返回值类型 方法(参数类型 参数名){
...
方法体
...
return 返回值;
}
方法头部分
修饰符:修饰符。可选的,告诉编译器如何调用该方法,定义了该方法的访问类型。
方法名:是方法的实际名称。方法名和参数表共同构成方法签名
参数类型:参数像一个占位符。当方法被调用时,传递至给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
-
形式参数:在方法被调用时用于接收外界输入的数据
-
实参:调用方法是实际传给方法的数据
方法调用
Java支持两种调用的方式:
根据方法是否返回值来选择
1、当方法返回一个值时,方法调用通常被当做一个值。例如:
int M = MAX(3,2);
2、如果方法返回值是void,方法调用一定是一条语句。例如:
System.out.printlb("Hello,kuangshen!");
方法的重载
重载是什么
重载就是在一个类中,有相同的函数名称,但参数不同
方法重载的规则
-
方法名称必须相同
-
-
方法的返回类型可以相同也可以不相同
-
仅仅返回类型不同不足以成为方法的重载
注意事项
方法名称相同时,编译器会根据调用方法的参数个数、参数类型等逐个匹配,以选择对应的方法,如果匹配失败,则编译器报错。
命令行传参
有时候你希望运行一个程序的时候再传递给它消息。这靠传递命令行参数给main()函数实现。
package com.li.method;
/*命令行传参*/
public class Demo03 {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println("args[" + i + "]" + args[i]);
}
}
}
可变参数
从JDK1.5开始,Java支持传递同类型的可变参数给一个方法。
在方法声明中,在指定参数类型后加一个省略号(...)
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
package com.li.method;
/**
* 可变参数
*/
public class Demo04 {
public static void main(String[] args) {
printMax(1,2,3);
printMax(new double[]{1,2,3});
}
//可变参数
public static void printMax(double... numbers){
if (numbers.length == 0){
System.out.println("没有元素通过");
return;
}
double result = numbers[0];
//排序!
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] > result){
result = numbers[i];
}
}
System.out.println("最大的值为:" + result);
}
}
练习题:实现计算器
递归
递归
A方法调用B方法,容易理解
A方法调用A方法!就是递归
package com.li.method;
//递归
public class Demo05 {
//递归就是思想的学习,栈的学习
public static void main(String[] args) {
System.out.println(f(100));
}
public static int f(int a){
if (a == 1){
return 1;
}else {
return a*f(a-1);
}
}
}
练习题:实现计算器
package com.li.method;
//计算器
import java.util.Scanner;
/**思路推荐:
写四个方法:加减乘除
利用循环+switch进行用户交互
传递需要操作的两个数
输出结果
**/
public class Demo06 {
public static void main(String[] args) {
System.out.println("请输入两个数");
int a = 3;
int b = 3;
String logo = "+";
switch(logo){
case "+":
System.out.println(a+b);
break;
case "-":
System.out.println(a-b);
break;
case "*":
System.out.println(a*b);
break;
case "%":
System.out.println(a/b);
break;
}
}
}
递归优点
可以用简单的程序来解决一些复杂的问题。
它通常把一个大型复杂的问题层层转换为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要多次重复计算,大大减少了程序的代码量。
递归的能力在于用有限的语句来定义对象的无限集合。
递归结构
- 递归体:什么时候不调用自身方法。如果没有头,将会陷入死循环
Java数组
数组的定义
- 数组是相同类型数据的有序集合
数组声明创建
声明数组语法
//以前为了吸引C和C++程序员转行的写法
int arr[]
//定义数组 首选
int[] arr;
创建数组语法
//使用new操作符来创建数组创建一个数组
arr = new int[10];
//数组的元素是通过索引访问的,数组索引从0开始
arr.length
数组的内存分析
数组是一种引用内存(heap),数组引用变量只是一个引用,数组元素和数组变量是分开的,数组对象(数组元素)存在堆内存中,数组变量是在栈内存中

数组三种初始化
数组静态初始化
//静态初始化
int[] arra = {1,2,3};
System.out.println(arra);
数组动态初始化
//动态初始化
int[] arrb = new int[10];
arrb[0]=1;
arrb[1]=2;
System.out.println(arrb);
System.out.println(arrb[0]);
数组的默认初始化
数组是引用类型,他的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。
下标越界
数组边界
//下标的合法区间:[0,length-1],如果越界就会报错
java.lang.ArrayIndexOutOfBoundsException
多维数组
多维数组可以看成是数组的数组,例如二维数组就是一个特殊的以为数组,其每一个元素都是一个一维数组。
//二维数组
int[][] = new int[2][5];
Array类
基本介绍
- 数组的工具类Java.util.Arrays
具体功能
-
给数组赋值:fill
-
-
比较数组:equals
-
查找数组元素:binarySearch方法能对排序好的数组进行二分查找法操作
冒泡排序
思路
两层循环,外环比较冒泡轮数,内存依次比较互换位置
时间复杂度
一般嵌套循环的复杂度为O(n2)
package com.li.arr;
import java.util.Arrays;
public class Demo07 {
//冒泡排序
public static void main(String[] args) {
int[] sorts = {1,23,4,5,1,2,34,5};
int[] sort = sort(sorts);
System.out.println(Arrays.toString(sort));
}
public static int[] sort(int[] arrays){
//临时变量
int temp = 0;
//外循环 判断次数
for (int i = 0; i < arrays.length-1; i++) {
//通过flag表示位减少没有意义的比较
boolean flag = false;
//内循环 比较两个数的大小,然后互换位置,知道最大或最小的出来
for (int j = 0; j < arrays.length-1-i ; j++) {
if (arrays[j]<arrays[j+1]){
temp = arrays[j];
arrays[j] = arrays[j+1];
arrays[j+1] = temp;
}
}
if (flag == false){
break;
}
}
return arrays;
}
}
稀疏数组
问题要求
-
编写五子棋的游戏中,有存盘和续上盘的功能
思路
- 在使用二维数组过程中有很多值是默认值0.因此记录了很多没有意义的数据,使用稀疏数组
稀疏数组的处理方式
- 常用于一个数组中大部分元素为0,或者为同一值得数组,可以使用稀疏数组来保存该数组
-
记录数组一共有几行几列,有多少个不同值
把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序规模
package com.li.arr;
//稀疏数组
public class Demo08 {
public static void main(String[] args) {
//创建新的棋盘
int[][] array1 = new int[11][11];
array1[1][2] = 1;
array1[2][3] = 2;
//输出原有数组
System.out.println("输出原有数组");
//遍历二维数组
for (int[] ints: array1) {
for (int anInt : ints) {
System.out.print(anInt+"\t");
}
System.out.println();
}
//获取有效值的个数
//将转换成稀疏数组、更像是重新创建一个数组
int sum = 0;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if (array1[i][j]!=0){
sum++;
}
}
}
//获取有效值的个数
System.out.println("获取有效值的个数"+sum);
//行数:有效值个数+1
//列数:固定的
//值:这还用说
//创建一个稀疏数组
int[][] array2 = new int[sum+1][3];
array2[0][0] = 11;
array2[0][1] = 11;
array2[0][2] = sum;
//遍历二维数组,将非零的值存放到稀疏数组中
int count = 0;
for (int i = 0; i < array1.length; i++) {
for (int j = 0; j < array1.length; j++) {
if (array1[i][j]!=0){
count++;
//从第几行第几列存取 棋盘中的位置信息
array2[count][0] = i;
array2[count][1] = j;
array2[count][2] = array1[i][j];
}
}
}
//输出稀疏数组
System.out.println("输出稀疏数组");
//因为稀疏数组列是固定的,只用把行中的元素遍历出来即可
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i][0] + "\t"
+array2[i][1] + "\t"
+array2[i][2] + "\t");
}
//还原稀疏数组
//1、读取稀疏数组
int[][] array3 = new int[array2[0][0]][array2[0][1]];
//2、给其中的元素还原他的值
for (int i = 1; i < array2.length; i++) {
array3[array2[i][0]][array2[i][0]] = array2[i][2];
}
//3、打印
System.out.println("还原的数组");
for (int[] intss: array3) {
for (int anIntt : intss) {
System.out.print(anIntt+"\t");
}
System.out.println();
}
}
}
面向对象
什么是面向对象?
Java的核心思想是OOP(Object-Oriented Programming,OOP)
首先区分面向过程和面向对象
面向过程:步骤清晰,第一步做什么,第二步做什么,面向过程适合处理一些较为简单的问题
面向对象思想:物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考,最后才对某个分类下得细节进行面向过程的思索。
但对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来拆分整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
总结
面向对象编程的本质就是:以类的方式组织代码,以对象的方式(封装)数据
类与对象的关系
-
-
对象是抽象概念的具体实例
package com.oop.demo02;
public class Person {
/*
构造器:
1、和类名相同
2、没有返回值
作用:
1、new本质是在调用构造方法
2、初始化对象的值
注意点:
1、定义有参构造后,如果想使用有参构造,必须有无参构造
*/
//一个类即使什么都不写,它也会存在一个方法
//显示的定义构造器
String name;
//无参构造
//1、使用new关键字,本质是在调用构造器
//2、用来初始化值
public Person(){
}
//有参构造:一旦定义了有残构造,无参构造必须定义
//无参构造即使啥都不写都可以,但是必须有,不然跑不了
public Person(String name){
this.name = name;
}
}
package com.oop.demo02;
public class Student {
//属性:字段
String name;
int age;
//方法
public void study(){
System.out.println(this.name+"在学习");
}
}
package com.oop.demo02;
public class Application {
public static void main(String[] args) {
/**
* 类:抽象的,实例化
* 类实例化后会返回一个自己的对象
* student对象就是一个Student类的具体实例
*/
Student jack = new Student();
Student tom = new Student();
jack.age = 19;
jack.name = "jack";
System.out.println(jack.name+jack.age);
tom.age = 18;
tom.name = "tom";
System.out.println(tom.name+tom.age);
//构造器
Person person = new Person();
System.out.println(person.name);
}
}
构造器详解
- 使用new关键字创建对象
- 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
1、必须和类的名字相同
3、分配内存:创建一个类 Student student = new Student();
- 定义的变量名student:变量名储存在栈区
- new:会在堆区开辟一个专属的空间用于存储数据

封装
-
高内聚:就是类的内部数据操作细节自己完成,不允许外部干涉
-
低耦合:仅暴露少量的方法给外部使用
封装(数据的直接隐藏),通常应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,称为信息隐藏,就像遥控器
封装的特点
1、提高程序的安全性,保护数据
3、统一接口
4、系统可维护增加了
package com.oop.demo04;
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.setName("里牧之");
System.out.println(student.getName());
}
}
package com.oop.demo04;
public class Student {
//提供一些可以操作这个属性的方法
//提供一些public的get、set方法
String name;
int id;
boolean sex;
//get 获得这个数据
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 boolean isSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
}
继承
继承的本事是对某一批类的抽象,从而实现对现实世界更好的建模。
extends的意思是“扩展”,子类是父类的扩展,Java中类只有单继承没有多继承!
继承关系的两个类
- 一个为子类(派生类)
- 一个为父类(基类)
子类继承父类,使用关键字extends来表示,object类 所有类均继承该类
package com.oop.demo05;
public class A extends B{
@Override
public void say() {
System.out.println("儿子");
}
}
package com.oop.demo05;
public class B {
public void say(){
System.out.println("爸爸");
}
}
package com.oop.demo05;
public class Application {
public static void main(String[] args) {
//方法的调用只和左边 定义的数据类型有关
A a = new A();
a.say();
//父类也可以指向子类
B b = new B();
b.say();
}
}
super
参考链接:https://blog.csdn.net/u012518665/article/details/80353225,写的真的不错
定义:它是一个指代变量,用于在子类中指代父类对象。
应用范围:只能用于子类的构造函数和实例方法中,不能用于子类的类(静态)方法中。原因是super指代的是一个父类的对象,它需要在运行时被创建,而静态方法是类方法,它是类的一部分。当类被加载时,方法已经存在,但是这时候父类对象还没有被初始化。
用法1:在子类中调用父类的属性或方法
public class Parent {
public String name = "小头爸爸";
public boolean hasMoney = false;
public void sleeping(){
System.out.println("爸爸已经睡了");
}
public void working(){
System.out.println("爸爸正在工作。。。");
}
}
public class Child extends Parent {
public String name = "大头儿子";
/**
* 当子类和父类有属性重名时,需要super才能调用到父类的属性,
* 直接调用会调用到子类的属性
* 如果不重名,可直接调用且调用的是父类的属性
*/
private void testParam(){
System.out.println("爸爸的名字叫" + super.name);
System.out.println("孩子的名字是" + name);
System.out.println("爸爸是否有有钱:" + hasMoney);
}
/**
* 方法和上面的属性结果一样
*/
public void testMethod(){
sleeping();
super.sleeping();
super.working();
}
public void sleeping(){
System.out.println("儿子已经睡了");
}
public static void main(String[] args) {
Child child = new Child();
child.testParam();
child.testMethod();
}
}
输入出结果:
=============
爸爸的名字叫小头爸爸
孩子的名字是大头儿子
爸爸是否有有钱:false
儿子已经睡了
爸爸已经睡了
爸爸正在工作。。。
============
用法2:在子类中指代父类构造器
在Java中,子类是父类的派生类,它的实例化依赖于父类的实例化。所以它的任何一个构造函数都必须要初始化父类,Java就是super关键字调用父类构造方法,来完成这个操作。
有人会奇怪,那为什么在用法1中,父类没有构造方法,子类也没有构造方法,难道在某些情况下,实例的初始化并不依赖于构造函数?
答案是,实例的初始化必须要构造函数。用法1中没有构造函数的原因是,在Java类定义中,如果开发者没有显示的定义构造函数,那么Java会隐式的为该类定义一个无参构造函数。但是如果开发者自己定一个构造函数(无论有参还是无参),那么Java就不会再为该类隐式的定义一个无参构造函数了。
那么还原构造函数后的代码:
public class Parent {
...
public Parent() {
}
...
}
public class Child extends Parent {
...
public Child() {
}
...
}
那么又有人有奇怪了,你不是说子类的实例化依赖于父类的实例化,那么在子类的构造函数中应该需要调用super来初始化父类不是吗?
是的,在我们子类的构造函数中,如果我们没有显式调用super来初始化父类的话,那么Java会隐式的调用super();来调用父类无参构造函数并把它放在构造函数的第一行。记住哦,Java只会隐式的调用无参构造函数,如果父类没有无参构造函数,那么子类中就必须显示的调用super关键字来调用已有的有参构造函数来初始化父类。
public class Parent {
public String name = "小头爸爸";
public Parent(int age) {
}
public void sleeping(){
System.out.println("爸爸已经睡了");
}
}
public class Child extends Parent {
public String name = "大头儿子";
public Child() {//如果没有super来初始化父类,同样也会报错
// super();//编译期报错,因为父类并没有无参构造函数
super(15);//正确打开方式
}
private void printParentName(){
System.out.println("爸爸的名字叫" + super.name);
}
private void parentStatus(){
super.sleeping();
}
}
子类中的super为什么必须要放在第一行?因为子类的实例化依赖于父类的实例化,在构建子类时,必须要有父类实例,只能有了父类的实例,子类才能够初始化自己。就好像人类世界里,都是要先有父亲,再有孩子一样。
总结:super关键字指代父类对象,主要用于在子类中指定父类的方法和属性,也用于在子类中初始化父类。子类的静态方法中不能使用super关键字。
方法重写
前提:需要有继承关系
1、方法名必须相同
2、参数列表必须相同
3、修饰符:范围可以扩大:public>protected>default>private
4、抛出的异常:范围可以被缩小但不能扩大 ClassNotFoundException-->Excpetion
为什么需要重写?
父类功能不满足使用要求,可以在IDE中使用快捷键重写方法
跟多态的关系?
1. 重写是多态的先决条件,没有重写,就没有多态
2. 类的属性上不存在多态
多态
即同一方法可以根据发送对象的不同而采用多种不同的行为方式,一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多。
多态存在的条件
-
有继承关系
-
子类重写父类方法
-
父类引用指向子类对象
例如static方法,属于类,但她不属于实例、final常量、private方法、这些不能重写的,就不存在多态。注意多态是方法的多态,属性是没有多态的
System.out.println(X instanceof Y);
//高--->低,这里Person是Student的父类
Person person = new Student();
//student将这个对象转换为Student类型,我们就可以使用Student类型的方法
((Student) person).go();
//低----------->高
Student student = new Student();
student.go();
Person person = student;
父类引用指向子类对象
把子类转换为父类(向上转型)
把父类转换为子类(向上转型) 注意:强制转换可能会造成方法丢失
类型转换的好处
- 方便方法的调用,减少重复的代码
static关键字
static:静态属性/变量、静态变量可以直接调用,非静态不可以
//静态变量与非静态变量
private static int age;
private double score;
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(Student.age);
System.out.println(Student.score);//错的
}
静态方法与非静态方法
非静态方法可以调用静态方法、但是静态方法不能调用非静态方法,仅限于静态方法之间的互相调用
原因:加载方式不一样,静态方法与类同时加载,非静态方法只有调用时才加载
public void run(){
}
public static void run(){
}
静态代码块与匿名代码块
运行顺序:静态代码块>匿名代码块>普通方法
{
//代码块(匿名代码块)
System.out.println("匿名代码块");
}
static {
//静态代码块
System.out.println("静态代码块");
}
public Person(){
System.out.println("普通方法");
}
静态导入包
仅仅是个写法,静态导入后不用写Math,直接可以输入需要的方法名
import static java.lang.Math.PI;
import static java.lang.Math.random;
public class Test {
public static void main(String[] args) {
//非静态导入
System.out.println(Math.random());
//静态导入
System.out.println(random());
System.out.println(PI);
}
}
抽象类
abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法、如果修改类,那么该类就是抽象类
抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类
注意
- 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的
- 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。
package com.oop.demo08;
//abstract 抽象类 抽象类只能单继承,接口可以单继承
public abstract class Action {
//约束:有人帮我们实现~
//抽象方法,只有方法的名字,没有方法的实现
public abstract void doSomething();
/*
1、不能new这个抽象类,只能靠子类去实现它:约束!
2、抽象类可以写普通的方法
3、抽象方法必须约束在抽象类中
思考?
抽象方法存在构造器吗?
抽象方法存在的意义在于,例如游戏开发过程中,过于
复杂的人物,避免错误时重复创建,而使用抽象类,提高开发效率
*/
}
package com.oop.demo08;
public class A extends Action{
public static void main(String[] args) {
}
@Override
public void doSomething() {
}
}
接口的定义与实现
- 普通类:只有具体的实现
- 抽象类:具体实现和规范(抽象方法)都有
- 接口:只有规范!自己无法写方法、专业的约束!约束和实现分离,面向接口编程
定义
接口就是规范,定义的是一组规则,体现了世界中国“如果你能。。则必须能。。”的思想。例如如果你是汽车,必须跑。
接口的本质是契约,就行人间的法律一样。制定好了后大家来遵守
package com.oop.demo09;
//抽象的思维很难,决定了未来的高度
//interface,接口都需要有实现类
public interface UserService {
//在接口中定义的是常量
//public static final int age = 10; 完整版
int age = 10;
//接口不能写方法
//接口中所有定义其实都是抽象的public abstract
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
package com.oop.demo09;
public class UserServiceImpl implements 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) {
}
}
内部类就是在一个类的内部定义了一个类,比如,A类中定义了一个B类,那么B类相对A类来说就称为内部类,而A类相对于B类就是外部类。
1、成员内部类
成员内部类
private int id;
public void out(){
System.out.println("外部类的方法");
}
public class Inner{
public void inner(){
System.out.println("这是内部类的方法");
}
//获取外部类的私有属性,算是成员内部类奇葩的方法。
public void getId(){
System.out.println(id);
}
}
main方法中
//通过外部类的方式来实例化
Outer.Inner inner = outer.new Inner();
2、静态内部类
public class Outer {
}
一个Java类中可以有多个class类,但是只能有一个public class
class A{
}
3、局部内部类
public class Outer {
//局部内部类
public void method(){
class Inner{
public void in(){
}
}
}
}
4、匿名内部类
public class Test {
//匿名内部类
public static void main(String[] args) {
//没有名字的初始化类,不用将实例保存在变量中
//Apple apple = new Apple();完整版
//比如接口重写方法时,也使用了匿名内部类
new Apple().eat();
new UserService() {
@Override
public void hello() {
}
};
}
}
class Apple{
public void eat(){
System.out.println("吃苹果");
}
}
interface UserService{
void hello();
}
异常
实际工作中,遇到的情况不可能是完美的,在软件程序运行过程中,非常可能晕倒例如文件不存在,文件格式不对,等等异常问题,英文:Excpetion,就是例外。
发生在程序运行期间,影响到了正常程序的执行流程。
异常的分类
- 检查型异常:比如用户错误或问题引起的异常,无法预见的。例如打开文件,但文件不存在。
package com.exception.demo01;
//运行时异常
public class Demo01 {
public static void main(String[] args) {
System.out.println(1/0);
}
}
package com.exception.demo01;
public class Test {
public static void main(String[] args) {
int a = 1;
int b = 2;
//主动抛出异常 throw throws
//主动抛出的异常,一般用在方法中
try {
new Test().test(2,0);
} catch (Exception e) {
e.printStackTrace();
}
}
//不加throws 无法抛出
public void test(int a,int b)throws ArithmeticException{
if (b == 0){//throw throws
throw new ArithmeticException(); //主动抛出的异常,一般用在方法中
}
}
}
// //ctrl+alt+t 实现语句的在自动包裹
// try {
// System.out.println();
// } catch (Exception e) {
// e.printStackTrace();
// }
// try {//监控区域
// System.out.println(a/b);
// }catch(ArithmeticException e){//捕获异常
// System.out.println("被除数不能为0");
// }
//
// //finally 可以不要。
// //处理善后工作
// finally {
// System.out.println("finally");
// }
异常的体系机构
在Java API中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception

Error
Error类对象由Java虚拟机生成并抛出,大多数和代码编写者无关。
还有发生在虚拟机试图执行应用时,入类定义错误(NoClassDerfFoundError)、链接错误(LinkageError)。这些错误时不可查的。因为它们在应用程序的控制和处理能力之外,而且绝大多数程序运行时不允许出现的状况。
Exception

异常处理的五个关键字
try、catch、finally、throw、throws
自定义异常
在程序中使用自定义异常类,大体可分为以下几个步骤:
1、创建自定义异常
2、在方法中通过throw关键字抛出异常对象
3、如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;
否在方法的声明出通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作。
4、在出现异常方法的调用者中捕获并处理异常
package com.exception.demo02;
//自定义异常
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 +
'}';
}
}
package com.exception.demo02;
public class Test {
static void test(int a) throws MyException {
System.out.println("传递的参数为:" + a);
if(a>10){
//抛出
throw new MyException(a);
}
System.out.println("Ok");
}
public static void main(String[] args) {
try {
test(11);
} catch (MyException e) {
e.printStackTrace();
}
}
}
总结
- 处理运行时异常时,采用逻辑去合理规避同时辅助try-catch处理
- 在多重catch块后,可以加一个catch(Excption)最大的范围来处理可以会被遗漏的异常
- 对于不确定的代码,也可以加上try-catch,处理潜在异常
- 具体如何处理异常,需根据不同的业务需求和异常类型去决定
- 尽量添加finally语句块去释放占用的资源

浙公网安备 33010602011771号