java基础二-狂神说
分析程序
1、符号 /* / 指示中间的语句是该程序中的注释。多行注释以 / 开始,以 */ 结束。单行注释以 // 开始,以行末结束
java文档注释使用/** */。
2、关键字 class 声明类的定义,还帮助编译器理解它是一个类的声明。
3、整个类及其所有成员都是在一对大括号中(即 { 和 } 之间)定义的。它们标志着类定义块的开始和结束。
4、程序从 main( ) 方法开始执行。
5、关键字 public 是一个访问说明符,控制类成员的可见度和作用域。
6、关键字 static 允许调用 main( ) 方法,而无需创建类的实例。
7、关键字 void 告诉编译器 main( ) 方法在执行时不返回任何值。
8、main( )方法是所有Java 应用程序的起始点。
9、args[ ]是String类型的数组。
10、println( )方法通过 System.out 显示作为参数传递给它的字符串。
常见问题
- javac不是内部命令或者可执行文件
在DOC命令下,可以运行java命令,但是在编译源代码时却不能运行javac命令,出现“javac不是内部命令或者可执行文件”,出现这种情况一般是由于Path的环境出问题,输入任何路径回去Path设定路径去找这些命令。 - java命令不能运行class
javac能正常编译,但是在输入 java helloWorld运行是去报出了下面一行代码提示。“Exception in thread "main" java.lang.NoClassDefFoundError: Hello World”提示根据提示,意思是找不到class文件。这个问题应该就是是classpath环境变量配置不对,检查你的classpath的设置,注意一些特殊的符号。然后重启DOS再试一次。
java虚拟机,JDK,JRE
- java虚拟机是一个可以执行java字节码的虚拟机进程。java源文件被编译成能被java虚拟机执行的字节码文件。
java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每个平台单独重写或者重新编译。Java虚拟机让这个变为可能,因为它底层硬件平台的指令长度和其它特性。 -
JRE:java Runtime Environment
-
JDK:Java Development Kit
- JRE顾名思意是java运行时需要的环境,包含了java虚拟机,java基础类库。是使用java语言编写的程序运行所需要的软件环境,是提供给运
行java程序的用户使用的。 - JDK:顾名思义是java开发工具包,是程序员使用java语言编写java语言编写java所需的开发工具包,是提供给程序员使用的。JDK包含了JRE,用时还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具:jconsole,jvisualvm等工具软件,还包含了java程序所编写所需的文档和dome例子程序。
- 如果需要运行java程序,只需要安装JRE就可以了。如果你需要编写java程序,需要安装JDK.
- JRE根据不同的操作系统(如:window,linux等)和不用JRE提供商(IBM,ORACLE等)有很多版本。
方法详解
方法解释
-
System.out.println(),做什么的?
-
//System"类".out“输出对象”.println()“方法”
-
java方法是语句的集合,他们在一起执行一个功能。
1. 方法是解决一类问题的步骤的有序组合
2. 方法包含于类或对象中
3. 方法在程序中被创建,在其他地方呗引用 -
程序方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。我们设计方法的时候,最好保持方法的原子性,就是一个方法只完成1个功能,这样利于我们后期的扩展。
-
maim尽量干净简洁,把公共模块提取到外面,利用方法调用。
-
回顾:方法的命名规则?
-
加强练习
public static void/*代表空类型*/ main(String[] args) {
//实际参数,实际调用传输给他的参数
int sum = add(1,2);
System.out.println(sum);
}//形式参数,用来定义作用的
public static int/*返回类型*/ add(int a,int b){
return a+b;//返回值
}
方法的定义
- Java的方法类似于其它语言的函数,是一段用于完成特定功能的代码片段 ,一般情况下,定义一个方法包含以下语法
- 方法包含一个方法头和一个方法体。下面是一个方法的所有部分:
- 修饰符:修饰符,这是可以选的,告诉编译器如何调用该方法。定义了该方法的访问类型
- 返回值类型方法可能会返回值,returnValueType 是方法返回值的数据类型,有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType 是关键字void。
- 方法名:是方法的实际名称。方法名和参数表共同构成方法签名。
- 参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
1. 形式参数:在方法被调用时用于接收外界输入的数据。
2. 实参:调用方法时实际传给方法的数据。
- 方法体:方法体包含具体的语句,定义该方法的功能。
修饰符 返回值类型 方法名(参数类型 参数名){
...
方法体
...
return 返回值;
}
- return
public static void main(String[] args){
max(20,20);
}
public static int max(int num1, int num2){
int result = 0;
if (num1==num2){
System.out.println("数据相等");
return 0;
}
if (num1>num2){
result = num1;
System.out.println(result);
}
if (num2>num1){
result = num2;
System.out.println(result);
}return result;
}
方法调用
- 调用方法:对象名.方法名(实参列表)
- Java 支持两种调用方法的方式,根据方法是否返回值来选择。
- 当方法返回一个值的时候,方法调用通常被当做一个值。例如:
int larger = max(30,40);
- 如果方法返回值是 void ,方法调用一定是一条语句。
System.out.println("Hello,kuangshen!");
- 课后拓展了解:值传递(Java)和引用传递。
值传递:使用两个不同的存储单元,执行中,形式参数值改变不会影响实际参数值。
引用传递:实际为引用地址,实际参数和形式参数指向同一地址,执行中形式参数会影响实际参数。
方法的重载
- 重载就是在一个类中,有相同的函数名称,但形参不同的函数。
- 方法重载的规则:
1. 方法名称必须相同。
2. 参数列表必须不同(个数不同、或类型不同、参数排列顺序不同等)。
3. 方法的返回类型可以相同也可以不相同。
4. 仅仅返回类型不同不足以成为方法的重载。 - 实现理论:
1. 方法名称相同时,编译器会根据调用方法的参数个数,参数类型等去逐个匹配,以选择对应的方法,如果匹配失败,则编译器报错。
public static void main(String[] args) {
max(11,11,11);
}
//二整数比大小
public static int max(int max1,int max2){
int result = 0;
if (max1==max2){
System.out.println("两值相等");
return 0;
}if (max1>max2){
result = max1;
System.out.println(result);
}if (max2>max1){
result = max2;
System.out.println(result);
}return result;
}
//二小数比大小
public static double max(double max1,double max2){
double result = 0;
if (max1==max2){
System.out.println("两值相等");
return 0;
}if (max1>max2){
result = max1;
System.out.println(result);
}if (max2>max1){
result = max2;
System.out.println(result);
}return result;
}
//三小数比大小
public static double max(double max1,double max2,double max3){
double result = 0;
if (max1==max2 && max2==max3){
System.out.println("两值相等");
return 0;
}if (max1>max2 && max1>=max3){
result = max1;
System.out.println(result);
}
else if (max1>max3 && max1>=max2){
result = max1;
System.out.println(result);
}
else if (max2>max1 && max2>=max3){
result = max2;
System.out.println(result);
}else if (max2>max3 && max2>=max1){
result = max2;
System.out.println(result);
}else if (max3>max1 && max3>=max2){
result = max3;
System.out.println(result);
}else if (max3>max2 && max3>=max1){
result = max3;
System.out.println(result);
}return result;
}
命令行传参
- 有时候你希望运行一个程序时候再传递给他消息。这要靠传递命令行参数给main()函数实现。
public static void main(String args[]){
for(int i=0;i<args.length;i++){
System.out.println("args["+i+"]:"+agrs[i])
}
}
可变参数
- JDK 1.5 开始,Java支持传递同类型的可变参数给一个方法。
- 在方法声明中,在指定参数类型后加一个省略号(…)。
- 一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
public static void main(String[] args) {
printMax(34,3,3,2,56.5);
printMax(new double[]{1,2,3});
}
public static void printMax( double... numbers){
if (numbers.length ==0){
System.out.println("No argument passed");
return;
}
double result = numbers[0];
for (int i = 1; i < numbers.length;i++){
if (numbers[i] > result){
result = numbers[i];
}
}
System.out.println("The max value is " + result);
}
递归
- A 方法调用 B 方法,我们很容易理解!
- 递归就是:A 方法调用 A 方法!就是自己调用自己
- 利用递归可以用简单的程序来解决一些复杂的问题。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可貌似出解决过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。
- 递归结构包括两个部分:
- 递归头:什么时候不调用自身方法。如果没有头,将陷入死循环。
- 递归体:什么时候需要调用自身方法 - 递归适合小计算,如果太大量计算容易内存崩溃,死机。
public static void main(String[] args){
System.out.println(f(5));
}
public static int f(int n){
if (n==1){
return 1;
}else {
return n*f(n-1);
}
}
数组
数组的定义
- 数组是相同类型数据的有序集合。
- 数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。
- 其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们。
数组声明创建
- 首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法:
dataType[] arrayRefVar; // 首选的方法
或
dataType arrayRefVar[]; // 效果相同,但不是首选方法
- Java 语言使用 new 操作符来创建数组,语法如下:
ataType[] arrayRefVar = new dataType[arraySize];
- 数组的元素是通过索引访问的,数组索引从0开始
- 获取数组长度:
arrays.length
练习
public static void main(String[] args){
int[] nums; //1.首选声明一哥数组
nums = new int[10]; //2.创建一哥数组
//3.给数组元素中赋值
nums[0] = 1;
nums[1] = 2;
nums[2] = 3;
nums[3] = 4;
nums[4] = 5;
nums[5] = 6;
nums[6] = 7;
nums[7] = 8;
nums[8] = 9;
nums[9] = 10;
//计算所有元素的和
int sum = 0;
//获取数组长度:arrays.length
for (int i = 0; i < nums.length ; i++){
sum = sum + nums[i];
}
System.out.println("总和:"+sum);
}
内存分析
- Java内存分析:
三种初始化
- 静态初始化
//静态初始化:创建 + 赋值
int[] a = {1,2,3};
Man[] mans = {new Man(1,1),new Man(2,2)};
- 动态初始化
//动态初始化:包含默认初始化,未赋值前为0。
int[] a = new int[2];
a[0] = 1;
a[1] = 2;
- 数组的默认初始化
- 数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方法被隐式初始化。
数组的四个基本特点
- 其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
- 其元素必须是相同类型,不允许出现混合类型。
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型。
- 数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。
数组边界
- 下标的合法区间:[0,length-1],如果越界就会报错;
public static void main(String[] args){
int[] a=new int[2]; //a长度为2,下标表示为a[0],a[1].
System.out.println(a[2]);//a[2]以超出设定值
}
- ArraylndexOutOfBoundsException:数组下标越界异常!
- 小结:
- 数组是相同数据类型(数据类型可以为任意类型)的有序集合
- 数组也是对象。数组元素相当于对象的成员变量
- 数组长度的确定的,不可变的。如果越界,则报:ArrayindexOutofBounds
数组的使用
- 普通的For循环
- For-Each 循环
- 数组作方法入参
- 数组作返回值
- 练习
public static void main(String[] args) {
int[] arrays = {11,12,13,14,15};
//打印全部的数组元素
for (int i = 0 ; i<arrays.length ; i++){
System.out.println(arrays[i]);
}
System.out.println("====================");
//打印数组元素之和
int sum = 0;
for (int i = 0; i < arrays.length; i++) {
sum = sum +arrays[i];
}
System.out.println("sum="+sum);
System.out.println("=================");
//查找数组内最大元素
int max = arrays[0] ;
for (int i = 1; i < arrays.length; i++) {
if (max < arrays[i]){
max = arrays[i];
}
}
System.out.println("max="+max);
}
public static void main(String[] args) {
int[] arrays ={1,2,3,4,5};
//JDK1.5 没有下标
for (int array : arrays) {
System.out.print(array+" ");
}
System.out.println();
System.out.println("打印数组");
printArray(arrays);
System.out.println("反转数组:");
int[] reverse= reverse(arrays);
printArray(reverse);
}
//反转数组
public static int[] reverse(int[] arrays){
int[] reverse =new int[arrays.length];
for (int i = 0, j=reverse.length-1; i <arrays.length; i++,j--) {
reverse[j] = arrays[i];
}
return reverse;
}
//打印数组元素
public static void printArray(int[] arrays){
for (int i = 0; i <arrays.length ; i++) {
System.out.println(arrays[i]+" ");
}
}
多维数组
- 多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。
- 二维数组
int a[][] =new int[2][5];
- 解析:以上二维数组 a 可以看成一个两行五列的数组。
- 思考:多维数组的使用?
num[1][0];
练习:
public static void main(String[] args) {
int[][] array = {{1,2},{2,3},{3,4},{4,5}};
for (int i = 0; i <array.length ; i++) {
for (int j = 0; j <array[i].length ; j++) {
System.out.print(array[i][j]+" ");
}
}
}
Arrays 类
- 数组的工具类java.uti.Arrays
- 由于数组对象本身并没有什么方法可以供我们调用,但是 API 中提供了一个工具类 Arrays 供我们使用,从而可以对数据对象进行一些基本的操作。
- 查看 JDK 帮助文档
- Arrays 类中的方法都是 static 修饰的静态方法,在使用的时候可以直接使用类名进行调用,而“不用”使用对象来调用(注意:是“不用”而不是“不能”)
- 具有以下常用功能:
- 给数组赋值:通过 fill 方法。
- 对数组排序:通过 sort 方法,按升序。
- 比较数据:通过 equals 方法比较数组中元素值是否相等。
- 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
public static void main(String[] args) {
int[] a = {3,54,8,2,9,6,57,12,87};
//打印数组元素,Arrays.toString
System.out.println(Arrays.toString(a));
//数组排序操作
Arrays.sort(a);
System.out.println(Arrays.toString(a));
//数组填充
Arrays.fill(a,7);
System.out.println(Arrays.toString(a));
//选择填充
Arrays.fill(a,2,6,0);
System.out.println(Arrays.toString(a));
}
冒泡排序
- 冒泡排序无疑是最为出名的排序算法之一,总共有八大排序!
- 冒泡的代码还是相当简单的,两层循环,外层冒泡轮数,里层依次比较,江湖中人人尽皆知。
- 我们看到嵌套循环,应该立马就可以得出这个算法的时间复杂度为o(n2)。
public static void main(String[] args) {
int[] a = {23,5,6,76,8,1,2,9,34,76};
int[] dai = dai(a);
System.out.println(Arrays.toString(a));
}
public static int[] dai(int[] suzu){
int p = 0;
boolean flag = false;
for (int i = 0; i <suzu.length-1 ; i++) {//最大外循环
for (int j = 0; j <suzu.length-1-i ; j++) {//排序循环
if (suzu[j+1]<suzu[j]){
p = suzu[j];
suzu [j] = suzu[j+1];
suzu [j+1] = p;
flag = true;
}if (flag == false){//优化,提前结束。
break;
}
}
}return suzu;
}
稀疏数组
- 需求:编写五子棋游戏中,有存盘退出或续上盘的功能。
- 分析问题:因为该二维数组的很多值是默认值0,因此记录了很多没有意义的数据
- 解决:稀疏数组
稀疏数组介绍 - 当一个数组中大部分元素为0,或者为同一值得数组时,可以使用稀疏数组来保存该数组。
- 稀疏数组的处理方式是:
- 记录数组一共有几行几列,有多少个不同值
- 把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模
public static void main(String[] args) {
int[][] array1 = new int[11][11];//0代表空,1代表黑子,2代表白子
array1[1][2] = 1;//给黑子定位
array1[2][3] = 2;//给白子定位
//输出原始数据
System.out.println("原始数据:");
for (int[] ints : array1) {
for (int anInt : ints) {
System.out.print(anInt+" ");
}
System.out.println();
}
System.out.println("======================");
//转换为稀疏数组保存
//1.求和有效值的个数
int sum = 0;
for (int i = 0; i < array1.length; i++) {
for (int j = 0; j < array1.length; j++) {
if (array1[i][j]!=0){
sum++;
}
}
}
System.out.println("稀疏数组:");
//2.创建一个稀疏数组的数组
int[][] array2 = new int [sum+1][3];//确定第一条稀疏数组的数据
array2[0][0]= 11;
array2[0][1]= 11;
array2[0][2]= sum;
//3.将非零的值,存放在稀疏数组中
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("序 "+" 行 "+" 列 "+" 值 ");//打印数组
int xu = 0;
for (int[] ints : array2) {
System.out.print(xu+" ");
for (int anInt : ints) {
if (anInt<10){System.out.print(" "+anInt+" ");}
else { System.out.print(" "+anInt+" ");}
}
xu++;
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");
}*/
System.out.println("========================");
//转换成原始数据
System.out.println("还原原始数组:");
int[][] array3 = new int[array2[0][0]][array2[0][1]];//确认原始数组整体大小
for (int i = 1; i <array2.length ; i++) {
array3[array2[i][0]][array2[i][1]]=array2[i][2];//将数组的数据赋值
}
for (int[] ints : array3) {
for (int anInt : ints) {
System.out.print(anInt+" ");
}
System.out.println();
}
}
初始面向对象
面向过程&面向对象
面向过程思想
- 步骤清晰简单,第一步做什么,第二步、、、
- 面对过程适合处理一些较为简单的问题
面向对象的思想
- 物以类聚,分类的思维模式,思考问题首先会想解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类的细节进行面向过程的思索。
- 面向对象适合处理复杂的问题,适合需要多人协作的问题。
注对于描述复杂的事务,为了宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,任然需要面向过程的思维去出处理
什么是面向对象
-
面向对象编程(Object-Oriented Programming, OOP)
-
面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据。
-
核心:抽象
-
三大特性:
- 封装
- 继承
- 多态
-
从认知论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象。
-
从代码运行角度考虑是先有类后有对象。类是对象的模板。
回顾方法及加深
- 方法的定义
- 修饰符
- 返回类型
//main方法
public static void main(String[] args) {
}
/*
修饰符 返回值类型 方法名(...){
//方法体
return 返回值;
}
*/
public String sayHellp(){
return "hello,world";
}
public int max(int a ,int b ){
return a>b ? a : b ;//三元运算符!
}
-
break 和return 的区别(break:跳出switch,结束循环。return:结束循环,返回值)
-
方法名(注意规范,见名知其意)
-
参数列表(参数类型,参数名)
-
异常抛出
-
方法的调用:递归
- 静态方法
- 非静态方法
/*静态方法 stutic
输出:类名+方法名
*/
/*非静态方法
1.实例化这个类 new+类名+.方法名
2.对象类型 对象名 = 对象值;
类名+.方法名
*/
public static void main(String[] args) {
Student student = new Student();
student.say();
}
public static void a(){
b()//不可以调b
}//因为static静态方法是和类一起加载,在创建的时候就已经有了
public void b(){
a();//可以调a
}//而非静态需要类实例化以后(new)才存在
- 形参和实参
- 值传递和引用传递
//引用传递:对象,本质还是值传递
public static void main(String[] args) {
Perosn perosn = new Perosn();
System.out.println(perosn.name);//null
Demo03.change(perosn);
System.out.println(perosn.name);//上下没中
}
public static void change(Perosn erosn){
//perosn 是一个对象:指向的 ---> Perosn perosn = new Perosn();
// 这是一个具体的人,可以改变属性
erosn.name = "上下没中";//它赋值的是
}
}
//定义一个perosn类,有一个属性:name
class Perosn{
String name;
- this关键字(代表当前这个类)
类与对象的关系
- 类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物。
- 动物、植物、手机、电脑、
- person类、pet类、car类等,这些类都是用来描述/定义某一类具体的事物应该具有的特点和行为
对象是抽象概念的具体实例 - 张三就是人的一个具体实例,张三家里的旺财就是狗的一个具体实例。
- 能够体现出特点,展现出功能的是具体的实例,而不是一个抽象的概念。
创建与初始化对象
-
使用new关键字创建对象
-
使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用。
一个类里面只有属性和方法。
//类是抽象的,必须用new实例化
//类实例化后会返回一个自己的对象
//返回后的对象就是抽象类的具体实例
- 类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下俩个特点:
- 必须和类的名字相同
- 必须没有返回类型,也不能写void
//一个类即使什么都不写,它也会存在一个方法(无参构造)
//显示的定义构造器
String name;
int age;
//1.使用new关键字,本质是在调用构造器
//2.无参构造器,用来初始化值
public Demo02(){
}
//一旦定义了有参构造,无参就必须显示定义
public Demo02(String name){
this.name = name;
}
//alt + insert 生产构造器
近期总结
- 类与对象
类是一个模板、抽象的;对象是一个具体的实例。
- 方法
定义与调用!
- 对应的引用
引用类型:
基本类型(8)
对象是通过引用来操作的:栈—>堆
- 属性:字段Field 成员变量
默认初始化:
数字: 0 0.0
char : u0000
boolean : false
引用 : null
修饰符 属性类型 属性名 = 属性值!
- 对象的创建和使用
必须使用 new 关键字创造对象,构造器 Person sxmz = new person();
对象的属性 sxmz.name
对象的方法 sxmz.sleep()
6. 类:
静态的属性 属性
动态的行为 方法
类里只写这两个。
封装
-
该漏的漏,该藏的藏
- 我们程序设计要追求“高内聚,低耦合”。高内聚就是将类的内部数据操作细节自己完成,不允许外部的干涉。
- 低耦合:尽量暴露少量的方法给外部使用。
-
封装(数据的隐藏)
- 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
-
属性私有,private;get/set;
public class Student {
//类 private:属性私有
private String name;
private int age;
private char sex;
//提供一些可以操作这个属性的方法
//public 的 get/set 方法
//get 获得这个数据
public String getName(){
return this.name;}
/*
Student s1 = new Student();
String name = s1.getName();
name = "小白";
System.out.println(name);
*/
//set 给这个数据设置值
public void setName(String name){
this.name = name;}
/*
Student s1 = new Student();
s1.setName("小黑");
System.out.println(s1.getName());
*/
//快捷键 alt + insert
继承
- 继承的本质是对某一批类的抽象,从而实现更好地建模
- extends的意思是扩展。子类是父类的扩展
- java中类只有单继承,没有多继承!
- 继承是类和类之间的关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
- 继承关系的俩个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。
- 子类和父类之间,从意义上讲应该具有“is a”的关系。
子类名 + extends + 父类名
Public 公共的
protected受保护的
default默认的
private私有的
public //公共的
protected //受保护的
default //默认的
private //私有的,无法被继承
//ctrl+h 继承树
- object类
在Java中,所有的类,都默认、直接或者间接继承object - super - this
this调用当前的类,super调用父类。
super注意点:
1. super调用父类的构造方法,必须在构造方法的第一个
2. super必须只能出现在子类的方法或者构造方法中
3. super和this不能同时调用构造方法
异同: - super:它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名(实参))
- this:它代表当前对象名(程序中易产生二易性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需要this来指明成员变量名)
- super()和this()类似,区别是,super()在子类中调用父类的构造方法,this()在本类内调用本类的其它构造方法。
- super()和this()均需放在构造方法内第一行。
- 尽管可以用this调用一个构造器,但却不能调用两个。
- this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
- this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。
- 从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。
- 方法重写Override
重写都是方法的重写,和属性无关。
重写值和非静态方法有关,静态没用,只能Public。
需要有继承关系,子类重写父类的方法!- 方法名必须相同
- 参数列表必须相同
- 修饰符:范围可以扩大,但不能缩小;public>protected>Default>private
- 抛出的异常:范围,可以被缩小但是不能扩大;ClassNotFoundException<-Exception(大)
重写,子类的方法和父类必须一致,但是方法体不同!
为什么需要从写
- 父类的功能,子类不一定需要,或者不一定满足!
快捷键:
Alt + Insert : override;
静态的方法和非静态的方法区别很大
多态
父类的引用可以指向子类,但不能调用子类独有的方法。
方法的调用只和左边定义的数据类型有关,和右边关系不大。
动态编译:类型:可扩展性更强
- 即同一方法可以根据发送对象的不同而采用多种不同的行为方式。
- 一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多(父类,有关系的类)
- 多态存在的条件
- 有继承关系的
- 子类重写父类方法
- 父类引用指向子类对象 - 注意:
多态是方法的多态,属于没有多态性
父类和子类,有联系才能转换,不然会异常!类型转换异常:ClassCastException
存在条件:继承关系,方法需要重写,父类引用指向子类对象!Father f1 = new son();
不能重写的方法:
- static 方法,属于类,它不属于实例
- final常量,被final修饰的无法修饰,属于常量池
- private私有方法,不能被重写
- instanceof 判断一个对象是什么类型。(类型转换一引用类型之间的转换)
System.out.println(x instanceof y); :true or false (能不能编译通过,看x所指向的实际类型是不是y的子类型)
注意:
- 父类引用指向子类的对象,不可以子类引用指向父类。
- 把子类转换为父类,向上转型;
- 把父类转换为子类,向下转型,强制转换
- 方便方法的调用,减少重复的代码,简介
//类型之间的转化 : 父---子
//高 低
Person s1 = new Student();
//高转低可以直接转;低转高,需要强制转
//
Student s2 = (Student) s1;
s2.go();
//或((Student) s1).go();
static关键字详解
private static int age;//静态的变量, 多线程
private double score; //非静态的变量
排序,static只执行一次
public class Demo03 {
//第二加载;适用于赋初值
{
System.out.println("匿名代码块");
}
//第一加载;只在第一次执行
static{
System.out.println("静态代码块");
}
//第三加载
public Demo03() {
System.out.println("构造方法");
}
public static void main(String[] args) {
Demo03 s1 = new Demo03();
System.out.println("==========");
Demo03 s2 = new Demo03();
}
}
静态导入包
//静态导入包
import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Demo03 {
public static void main(String[] args) {
System.out.println((int)(Math.random()*50));
//random()随机值,整数,范围(0-50)
//使用静态导入包后可以直接System.out.println(random());
System.out.println(PI);
}
}
final修饰的类不能被继承
抽象类
- abstract修饰符可以用来修饰方也可以修饰类,如果修饰方法,那么该方法就是抽象方法,如果修饰类,就是抽象类
- 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。
“Abstract method in non-abstract class” - 抽象类,不能使用 new 关键字来创建对象,它是用来让子类继承的。
- 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
- 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法;否者该子类也要声明为抽象类,然后由子子类实现抽象方法。
报错“Missing method body, or declare abstract”
接口
- 普通类:只有具体实现
- 抽象类:具体实现和规范(抽象方法都有)!
- 接口:只有规范!自己无法写方法,专业的约束!约束和实现分离:面向接口编程~
- 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。
“如果你是天使,则必须能飞。如果你是汽车,则必须能跑。” - 接口的本质是契约就像我们人间的法律一样。制定好后大家都遵守。
- OO的精髓,是对对象的抽象,最能体现这一点的就是接口,为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如c++、java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。
声明类的关键字是class,声明接口的关键字是interface
作用:
- 约束,规范
- 定义一些方法,让不同的人实现。多人完成共同的工作。
- 接口中所有默认的方式public abstract
- 所有常量默认public static final
- 接口不能被实例化,接口中没有构造方法
- 可以实现多个接口
- 必须要重写接口中的方法。
- 声明接口interface,实现接口implements,可以实现多个方法
import com.ssxxz.oop.Demo09.xiaduan;
public class Application {
public static void main(String[] args) {
xiaduan sxmz = new xiaduan();
sxmz.mingzi("上下没中");
sxmz.nianling(31);
sxmz.shengao(175.8);
sxmz.zhuzhi("内蒙古");
}
}
/*
public class xiaduan implements jiekou,jiekou2 {
@Override//jiekou
public void mingzi(String name) {
System.out.println("名字:"+name);
}
@Override//jiekou
public void nianling(int nl) {
System.out.println("岁数:"+nl);
}
@Override//jiekou
public void shengao(double sg) {
System.out.println("身高:"+sg);
}
@Override//jiekou2
public void zhuzhi(String zz) {
System.out.println("住宅:"+zz);
}
}
*/
内部类(扩展知识)
- 内部类就是在一个类的内部在定义一个类,比如A类定义了一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了。
- 成员内部类;类中加类
//先实例化外部类,再用外部类实例化内部类
Outer outer = new Outer();//new外部
Outer.Inner inner = outer.new Inner();//new内部
inner.in();
内部类可以获得外部类的私有属性
2. 静态内部类;不能直接访问非静态的属性(static先与非静态类生成)
3. 局部内部类;
public void method(){
class inner{
public void in(){
}
}
}
- 匿名内部类;没有名字去初始化类,不用将实例保存到变量中
//不起名直接使用
new Apple().eat();
//new接口
class Test{
public static void main(String[] args) {
new UserService(){
};
}
}
interface UserService{
}
一个java类中,可以有多个 class 类,但只能有一个 public class 类
异常机制
异常定义
- 实际工作中,遇到的情况不可能是非常完美的。比如:你写的某个模块,用户输入不一定符合你的要求;你的程序要打开某个文件,这个文件可能不存在或者文件格式不对;你要读取数据库的数据,数据可能是空的等;我们的程序再跑着,内存或硬盘可能满了。等等。
- 软件程序在运行过程中,非常可能遇到刚刚提到的这些异常问题,我们叫异常,英文是:Exception,意思就是例外。这些,例外情况,或者叫异常,怎么让我们写的程序做出合理的处理,而不至于程序崩溃。
- 异常值程序运行中出现不期而至的各种状况,如:文件找不到,网络连接失败,非法参数等。
- 异常发现在程序运行期间,它影响了正常的程序执行流程。
简单分类 - 要理解 Java 异常处理是如何工作的,你需要掌握以下三种类型的异常:
- 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。
- 例如:打卡一个不存在文件时,一个异常就会发生了,这些异常在编译时不能被简单地忽略。
- 运行时异常:运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- 错误 ERROR:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。编译时不容易被发现。
例如:当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
异常体系结构
-
Java把异常当前对象来处理,并定义一个基础类java.lang.Throwable 作为所有异常的超类。
-
在 Java API 中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception 。
Error -
Error类对象有Java虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。
-
Java虚拟机运行错误,当JVM不在有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时, Java 虚拟机(JVM)一般会选择线路终止。
-
还有发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError)、链接错误(LinkageError)。这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的情况。
Exception -
在 Exception 分支中有一个重要的子类 RuntimeException (运行时异常)
- ArrayIndexOutOfBoundsException(数组下标越界)
- NullPointerException(空指针异常)
- ArithmeticException(算术异常)
- MissingResourceException(丢失资源)
- ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。 -
Error 和 Exception 的区别:Error 通常是灾难性的致命错误,是程序无法控制和处理的,当出现这些异常时,Java 虚拟机(JVM)一般会选择终止线程;Exception 通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这类异常。
异常处理机制
- 抛出异常
- 捕获异常
- 异常处理五个关键字
- try、catch、finally、throw、throws
- 可以当出现异常时,捕获它,防止程序停止。
public static void main(String[] args) {
int a = 1;
int b = 0;
//假设要捕获多个异常:从小到大!
try { //try 监控区域
System.out.println(a / b);
} catch (Error e) { //catch(想要捕获的异常类型!)捕获异常
System.out.println("Error");
}catch (Exception e){//“e”,代表异常消息
System.out.println("Exception");
}catch (Throwable t){
System.out.println("Throwable");
}finally { //处理善后工作,不管报不报异常,都会执行。
System.out.println("finally");
}
//finally 可以不用,但是catch必须有。finally假设Io,资源,关闭工作
//快捷键:选中需要包裹的代码,ctrl+alt+T
/*
try {
System.out.println(a / b);
} catch (Exception e) {
System.exit(0);//程序结束
e.printStackTrace();//打印错误的栈信息
} finally {
}
*/
}
throw
try {
if (b==0){
throw new ArithmeticException();//主动的抛出异常
}
throws
public static void main(String[] args) {
try {
new linshi().test(1,0);
} catch (ArithmeticException e) {
e.printStackTrace();
} finally {
}
}
//假设这个方法中,处理不了这个异常。方法上抛出异常throws,由上一级捕获。
public void test(int a,int b)throws ArithmeticException{
if (b == 0) {
throw new ArithmeticException();//throw 主动的抛出异常,一般在方法内
}
System.out.println(a / b);
}
自定义异常
- 使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外用户还可以自定义异常。用户自定义异常类,只需继承 Exception 类即可。
- 在程序中使用自定义异常类,大体可分为以下几个步骤:
1. 创建自定义异常类。
2. 在方法中通过 throw 关键字抛出异常对象。
3. 如果在当前抛出异常的方法中处理异常,可以使用 try-catch 语句捕获并处理;否则在方法的声明处通过 throws 关键字指明要抛出给方法调用者的异常,继续进行下一步操作
4. 在出现异常方法的调用者中捕获并处理异常。
自定义异常类
//自定义异常类
//假设传递数字>10异常
private int tishi;//创建一个提示信息
public Demo01(int a) {//创建一个构造器传递消息
this.tishi = a;
}
//toString打印信息:异常的打印信息
@Override
public String toString() {
return "异常{" + "tishi=" + tishi + '}';
}
throws抛出方法捕获
//创建一个可能会存在异常的方法
static void test(int a) throws Demo01 {
System.out.println("传递的参数为:"+a);
if (a>10){
throw new Demo01(a);
}
System.out.println("ok");
}
public static void main(String[] args) {
try { //赋值并捕获
test(11);
} catch (Demo01 e) {
System.out.println("注意:"+e);
}
}
throw方法内捕获
//创建一个可能会存在异常的方法
static void test(int a) {
System.out.println("传递的参数为:"+a);
if (a>10){
try {
throw new Demo01(a);
} catch (Demo01 e) {
System.out.println("注意:"+e);;
}
}
System.out.println("ok");
}
public static void main(String[] args) {
test(15); //赋值
}
实际应用中的经验总结
- 处理运行时异常,采用逻辑去合理规避同时辅助 try-catch 处理
- 在多重 catch 块后面,可以加一个 catch(Exception)来处理可能被遗漏的异常
- 对于不确定的代码,也可以加上 try-catch ,处理潜在的异常
- 尽量去处理异常,切忌只是简单地调用 printStackTrace()去打印输出
- 具体如何处理异常,要根据不同的业务需求和异常类型去决定
- 尽量添加 finally 语句块去释放占用的资源