Java基础(上)

Java基础(上)


  • 学习一项技术之前,一定要知道它是做什么的!!!

    这项技术是什么 ?

    为什么要学这项技术 ?

    学这项技术有什么好处 ?


  • 有道无术,术尚可求,有术无道,止于术


  • 不要为了一些细节钻牛角尖

    • 位运算符,++ii++ 类似这种绕死人的语法

    • 内部类

    • AWT,SWING编程

    • 注解

      ......


  • 有些知识点需要深入理解

    • 流程控制

    • 面向对象思想

    • Java语法

      • this、方法重载&重写、finalstatic ......
    • 集合(包括泛型)

      • 常用的集合类
    • IO流

      • IO流代码的编写

      ......


  • 简单过一遍的知识点

    • 异常

    • 多线程

    • 网络编程

    • 反射机制

      ......

    要知道这些知识点是干嘛用的,能看懂具体的代码


Java学习路线


Java基础

流程控制 重点理解
面向对象 包括Java语法,重点理解
Java集合 重点理解
Java IO流 重点理解
异常 简单过一遍
多线程 简单过一遍
网络编程 简单过一遍
反射 简单过一遍
  • 简单过一遍并不是说不重要,而是日后再深入理解,毕竟现在时间有限

JavaWeb基础

HTML/CSS/JavaScript/jQuery 简单过一下
Tomcat 简单过一下
XML/注解 简单过一下
Servlet 重点理解
HTTP 重点理解
Filter过滤器 重点理解
Listener监听器 简单过一下
JSP 简单过一下
AJAXJSON 简单过一下

数据库

  • 创建表(create table)

  • 增删改查(insert,delete,update,select)

  • 对于存储过程、触发器这些了解一下即可

  • 对于索引、锁后面再学(此部分很重要,但以快速入门来说,可以先不看)


学习Java连接数据库(JDBC)

JDBC这项技术并不难,就是模板代码,来来去去就几个步骤:

  • 导入MySQL或者Oracle驱动包
  • 装载数据库驱动程序
  • 获取到与数据库连接
  • 获取可以执行SQL语句的对象
  • 执行SQL语句
  • 关闭连接

由于这些代码可能会重复出现,我们可以学习一下DbUtils这个组件:可以帮我们减少编写JDBC的模板代码。

以后我们会使用更强大的Mybatis


项目管理和框架的学习

经过上面的学习,已经是可以在本地写一个Web项目了

  • 页面框架使用BootStrap/Materialize框架来搭好
  • 请求处理交由Servlet,返回的数据可以通过AJAX或者使用JSP,DAO层可以使用DbUtils
  • 数据保存在MySQL

为了让写代码变得更爽,我建议用半天学一下Maven(项目管理工具),用几天学一下SpringBoot

  • Servlet直接跳到SpringBoot可能有点难理解,但多搞几天我相信还是可以的......

Linux学习

我们在本地写完的项目想要让外部访问的话,一般都会部署在Linux环境下的。(此部分的学习可以等到将项目写完,想要部署项目才学习)


开发环境的搭建

  • JDK的下载、安装
  • 环境变量的配置
  • 理解JDKJREJVM的关系
    • JDK = JRE + 开发工具集
    • JRE = JVM + Java SE标准类库

变量(重点)


按数据类型分类

  • 基本数据类型

    • 数值型
      • 整数类型:byteshortintlong
      • 浮点类型:doublefloat
    • 字符型:char
    • 布尔型:boolean

  • 引用数据类型

    • 接口
    • 数组

自动类型转换

当容量小的数据类型与容量大的数据类型做运算时,结果自动提升为容量大的数据类型。

byte charshort --> int --> long --> float --> double
特别的:当bytecharshort三种类型的变量做运算时,结果会自动提升为int


强制类型转换

需要使用强转符:()
强制类型转换,可能导致精度损失。


运算符(这里就不从头详细说明了)

开发中,如果希望变量实现+2的操作,有几种方法?(前提:int num = 10;)

//方式一
num = num + 2;

//方式二(推荐)
num += 2;

开发中,如果希望变量实现+1的操作,有几种方法?(前提:int num = 10;)

//方式一
num = num + 1;

//方式二
num += 1; 

//方式三(推荐)
num++; 

区分& 与 &&
相同点1:运算结果相同
不同点:当符号左边是false时,&继续执行符号右边的运算。&&不再执行符号右边的运算。
开发中,推荐使用&&


如果程序既可以使用三元运算符,又可以使用if-else结构,那么优先选择三元运算符。

原因:简洁、执行效率高。


流程控制(重点)


分支结构

  • if - else

    //结构一:
    if(条件表达式){
    	//执行表达式
    }
    
    ---------------------------------------
    
    //结构二:二选一
    if(条件表达式){
    	//执行表达式1
    }else{
    	//执行表达式2
    }
    
    ---------------------------------------
    
    //结构三:n选一
    if(条件表达式){
    	执行表达式1
    }else if(条件表达式){
    	执行表达式2
    }else if(条件表达式){
    	执行表达式3
    }
    ...
    else{
    	执行表达式n
    }
    
    1. else 结构是可选的
    2. 针对于条件表达式
      • 如果多个条件表达式之间是“互斥”关系(或没有交集),哪个判断和执行语句声明在上面还是下面,无所谓。
      • 如果多个条件表达式之间有交集的关系,需要根据实际情况,考虑清楚应该将哪个结构声明在上面。
      • 如果多个条件表达式之间有包含的关系,通常情况下,需要将范围小的声明在范围大的上面。否则,范围小的就没机会执行了。
    3. if-else结构是可以相互嵌套的
    4. 如果if-else结构中的执行语句只有一行时,对应的一对{}可以省略的。但是,不建议省略

  • switch-case
switch(表达式){
case 常量1:
	执行语句1;
	//break;
case 常量2:
	执行语句2;
	//break;
...
default:
	执行语句n;
	//break;
}
  1. 根据switch表达式中的值,依次匹配各个case中的常量。一旦匹配成功,则进入相应case结构中,调用其执行语句。当调用完执行语句以后,则仍然继续向下执行其他case结构中的执行语句,直到遇到break关键字或此switch-case结构末尾结束为止。

  2. break,可以使用在switch-case结构中,表示一旦执行到此关键字,就跳出switch-case结构

  3. switch结构中的表达式,只能是如下的6种数据类型之一:

    byteshortcharint枚举类型(JDK5.0新增)、String类型(JDK7.0新增)

  4. case 之后只能声明常量。不能声明范围。

  5. break关键字是可选的。

  6. default相当于if-else结构中的elsedefault结构是可选的,而且位置是灵活的。

  7. 如果switch-case结构中的多个case的执行语句相同,则可以考虑进行合并。


循环结构

  • 循环结构的四要素

    ① 初始化条件
    ② 循环条件 (是boolean类型)
    ③ 循环体
    ④ 迭代条件

    说明:通常情况下,循环结束都是因为②中循环条件返回false了。



  • 三种循环结构:

    1. for循环结构

      for(①;②;④){

      }
      执行过程:① - ② - ③ - ④ - ② - ③ - ④ - ... - ②



    2. while循环结构

      while(②){
      ③;
      ④;
      }
      执行过程:① - ② - ③ - ④ - ② - ③ - ④ - ... - ②

      注意:写while循环千万小心不要丢了迭代条件。一旦丢了,就可能导致死循环!



    3. do-while循环结构


      do{
      ③;
      ④;
      }while(②);
      执行过程:① - ③ - ④ - ② - ③ - ④ - ... - ②

      说明:
      1.do-while循环至少会执行一次循环体!
      2.开发中,使用forwhile更多一些。较少使用do-while


      forwhile循环总结:

      1. 开发中,基本上我们都会从forwhile中进行选择,实现循环结构。
      2. for循环和while循环是可以相互转换的
        区别:for循环和while循环的初始化条件部分的作用范围不同。
      3. 我们写程序,要避免出现死循环。


    4. “无限循环”结构: while(true) 或 for( ; ; )

      如何结束一个循环结构?

      • 当循环条件是false
      • 在循环体中,执行break


    5. 循环嵌套

      练习:

      ​ 九九乘法表

      ​ 100以内的质数


break和continue关键字

关键字 使用范围 循环中使用的作用(不同点) 相同点
break switch-case、循环结构中 结束当前循环 关键字后面不能声明执行语句
continue 循环结构中 结束当次循环 关键字后面不能声明执行语句

补充:带标签的break - continue(了解即可)


数组(重点)

数组相关的概念:

  • 数组名
  • 元素
  • 脚标
  • 数组的长度:元素的个数

数组的特点:

  • 数组是有序排列的
  • 数组属于引用数据类型的变量。数组的元素,既可以是基本数据类型,也可以是引用数据类型
  • 创建数组对象会在内存中开辟一整块连续的空间
  • 数组的长度一旦确定,就不能修改。

简单说一下数据的存储结构:

  • 线性表:顺序表(比如:数组)、链表、栈、队列
  • 树形结构:二叉树
  • 图形结构:

算法:

  • 排序算法
  • 搜索算法

一维数组的声明与初始化

int[] ids;//声明

//1.1 静态初始化:数组的初始化和数组元素的赋值操作同时进行
ids = new int[]{1001,1002,1003,1004};

//1.2动态初始化:数组的初始化和数组元素的赋值操作分开进行
String[] names = new String[5];

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

//		错误的方式:
//		int[] arr1 = new int[];
//		int[5] arr2 = new int[5];
//		int[] arr3 = new int[3]{1,2,3};

一维数组元素的引用:通过角标的方式调用。

		//数组的角标(或索引从0开始的,到数组的长度-1结束。
		names[0] = "王铭";
		names[1] = "王赫";
		names[2] = "张学良";
		names[3] = "孙居龙";
		names[4] = "王宏志";

数组的属性:length

System.out.println(names.length);//5
System.out.println(ids.length);

说明:
数组一旦初始化,其长度就是确定的
数组长度一旦确定,就不可修改


一维数组的遍历

for(int i = 0;i < names.length;i++){
	System.out.println(names[i]);
}

一维数组元素的默认初始化值

数组元素是整型:0

数组元素是浮点型:0.0

数组元素是char型:0或'\u0000',而非'0'

数组元素是boolean型:false

数组元素是引用数据类型:null


二维数组

一维数组中的每一个元素又是一个一维数组


二维数组的声明与初始化

//静态初始化
int[][] arr1 = new int[][]{{1,2,3},{4,5},{6,7,8}};

//动态初始化1
String[][] arr2 = new String[3][2];

//动态初始化2
String[][] arr3 = new String[3][];

//也是正确的写法:
int[] arr4[] = new int[][]{{1,2,3},{4,5,9,10},{6,7,8}};
int[] arr5[] = {{1,2,3},{4,5},{6,7,8}};

//      错误的方式:
//		String[][] arr4 = new String[][4];
//		String[4][3] arr5 = new String[][];
//		int[][] arr6 = new int[4][3]{{1,2,3},{4,5},{6,7,8}};

如何调用二维数组元素

System.out.println(arr1[0][1]);//2
System.out.println(arr2[1][1]);//null
		
arr3[1] = new String[4];
System.out.println(arr3[1][0]);
System.out.println(arr3[0]);

二维数组的属性

System.out.println(arr4.length);//3
System.out.println(arr4[0].length);//3
System.out.println(arr4[1].length);//4

遍历二维数组元素

for(int i = 0;i < arr4.length;i++){
			
			for(int j = 0;j < arr4[i].length;j++){
				System.out.print(arr4[i][j] + "  ");
			}
			System.out.println();
	}

二维数组元素的默认初始化值

规定:二维数组分为外层数组的元素,内层数组的元素
 * 		int[][] arr = new int[4][3];
 * 		外层元素:arr[0],arr[1]等
 * 		内层元素:arr[0][0],arr[1][2]等
 * 
 *   ⑤ 数组元素的默认初始化值 
 *   针对于初始化方式一:比如:int[][] arr = new int[4][3];
 *      外层元素的初始化值为:地址值
 *      内层元素的初始化值为:与一维数组初始化情况相同
 *      
 *   针对于初始化方式二:比如:int[][] arr = new int[4][];
 *   	外层元素的初始化值为:null
 *      内层元素的初始化值为:不能调用,否则报错。

数组的内存解析

(这里就不画图了,自行百度即可)


数组的常见算法

  • 数组的创建与元素赋值

    杨辉三角(二维数组)

    回形数(二维数组)

    1-30之间随机生成且不重复


  • 针对于数值型的数组

    最大值、最小值、总和、平均数等


  • 数组的赋值与复制

    int[] array1,array2;
    array1 = new int[]{1,2,3,4};
    
    //赋值
    //将array1保存的数组的地址值赋给了array2,使得array1和array2共同指向堆空间中的同一个数组实体。
    array2 = array1;
    
    //复制
    //我们通过new的方式,给array2在堆空间中新开辟了数组的空间。将array1数组中的元素值一个一个的赋值到array2数组中。
    array2 = new int[array1.length];
    for(int i = 0;i < array2.length;i++){
    	array2[i] = array1[i];
    }
    
    //数组元素的反转
    //		方法一:
    //		for(int i = 0;i < arr.length / 2;i++){
    //			String temp = arr[i];
    //			arr[i] = arr[arr.length - i -1];
    //			arr[arr.length - i -1] = temp;
    //		}
    		
    // 		方法二:
    //		for(int i = 0,j = arr.length - 1;i < j;i++,j--){
    //			String temp = arr[i];
    //			arr[i] = arr[j];
    //			arr[j] = temp;
    //		}
    
    //数组中指定元素的查找:搜索、检索
    //线性查找:通过遍历的方式,一个一个的数据进行比较、查找。
    //二分法查找:每次比较中间值,折半的方式检索。
    
    
    //手写冒泡排序
    int[] arr = new int[]{43,32,76,-98,0,64,33,-21,32,99};
    		
    		for(int i = 0;i < arr.length - 1;i++){
    			
    			for(int j = 0;j < arr.length - 1 - i;j++){
    				
    				if(arr[j] > arr[j + 1]){
    					int temp = arr[j];
    					arr[j] = arr[j + 1];
    					arr[j + 1] = temp;
    				}
    				
    			}
    			
    		}		
    

Arrays工具类的使用

① 定义在java.util包下。
Arrays提供了很多操作数组的方法。


		//1.boolean equals(int[] a,int[] b):判断两个数组是否相等。
		int[] arr1 = new int[]{1,2,3,4};
		int[] arr2 = new int[]{1,3,2,4};
		boolean isEquals = Arrays.equals(arr1, arr2);
		System.out.println(isEquals);
		
		//2.String toString(int[] a):输出数组信息。
		System.out.println(Arrays.toString(arr1));
		
			
		//3.void fill(int[] a,int val):将指定值填充到数组之中。
		Arrays.fill(arr1,10);
		System.out.println(Arrays.toString(arr1));
		

		//4.void sort(int[] a):对数组进行排序。
		Arrays.sort(arr2);
		System.out.println(Arrays.toString(arr2));
		
		//5.int binarySearch(int[] a,int key)
		int[] arr3 = new int[]{-98,-34,2,34,54,66,79,105,210,333};
		int index = Arrays.binarySearch(arr3, 210);
		if(index >= 0){
			System.out.println(index);
		}else{
			System.out.println("未找到");
		}

数组的常见异常

1.数组角标越界异常:ArrayIndexOutOfBoundsException

2.空指针异常:NullPointerException


面向对象


类与对象

  • 学习面向对象的三条主线:

    1.Java类及类的成员:属性、方法、构造器;代码块、内部类

    2.面向对象的大特征:封装性、继承性、多态性、(抽象性)

    3.其它关键字:thissuperstaticfinalabstractinterfacepackageimport


  • 面向对象中两个重要的概念:

    类:对一类事物的描述,是抽象的、概念上的定义
    对象:是实际存在的该类事物的个体,因而也称为实例(instance)

    面向对象程序设计的重点是类的设计
    设计类,就是设计类的成员。

    对象,是由类new出来的


  • 面向对象思想实现的规则

    1.创建类,设计类的成员

    2.创建类的对象

    3.通过“对象.属性”或“对象.方法”调用对象的结构


对象的创建与对象的内存解析

Person p1 = new Person();
Person p2 = new Person();
Person p3 = p1;//没有新创建一个对象,共用一个堆空间中的对象实体。

//如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static的)
//意味着:如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值。

匿名对象

我们创建的对象,没显式的赋给一个变量名。即为匿名对象

特点:匿名对象只能调用一次。


//举例:
	new Phone().sendEmail();
	new Phone().playGame();
		
	new Phone().price = 1999;
	new Phone().showPrice();//0.0

//应用场景:
	PhoneMall mall = new PhoneMall();

	//匿名对象的使用
	mall.show(new Phone());

	//其中,
    class PhoneMall{
        public void show(Phone phone){
            phone.sendEmail();
            phone.playGame();
        }

    }

类的结构之----属性

对比:属性 vs 局部变量

1.相同点:

  •  定义的格式:数据类型  变量名 = 变量值
    
  •  先声明,后使用
    
  •  变量都其对应的作用域 
    

2.不同点:

  • 在类中声明的位置的不同

    属性:直接定义在类的一对{}内

    局部变量:声明在方法方法形参代码块构造器形参构造器内部的变量


  • 关于权限修饰符的不同

    属性:可以在声明属性时,指明其权限,使用权限修饰符

    常用的权限修饰符:privatepublic缺省protected

    局部变量:不可以使用权限修饰符


  • 默认初始化值的情况:

    属性:类的属性,根据其类型,都默认初始化值

    整型(byteshortintlong:0)

    浮点型(floatdouble:0.0)

    字符型(char:0 (或'\u0000'))

    布尔型(booleanfalse

    引用数据类型(类、数组、接口:null

    局部变量:没有默认初始化值

    意味着,在调用局部变量之前,一定要显式赋值

    特别地:形参在调用时,我们赋值即可


  • 在内存中加载的位置:

    属性:加载到堆空间中 (非static

    局部变量:加载到栈空间


类的结构之----方法

方法:描述类应该具的功能。

//方法的声明:
//权限修饰符 返回值类型 方法名(形参列表){方法体}
//举例:
public void eat(){}
public void sleep(int hour){}
public String getName(){}
public String getNation(String nation){}
  • 说明:

    关于权限修饰符:默认方法的权限修饰符先都使用public

    Java规定的4种权限修饰符:privatepublic缺省protected

  • 返回值类型: 返回值 vs 没返回值

    如果方法有返回值,则必须在方法声明时,指定返回值的类型。同时,方法中,需要使用return关键字来返回指定类型的变量或常量

    如果方法没返回值,则方法声明时,使用void来表示。

  • 我们定义方法该不该返回值?

    ① 题目要求

    ② 凭经验:具体问题具体分析

  • 方法名:属于标识符,遵循标识符的规则和规范,“见名知意

  • 形参列表: 方法可以声明0个,1个,或多个形参。

    格式:数据类型1 形参1,数据类型2 形参2,...

  • 我们定义方法时,该不该定义形参

    ① 题目要求

    ② 凭经验:具体问题具体分析

  • 方法体:方法功能的体现。


return关键字

  • 使用范围:使用在方法体中

  • 作用:

    ① 结束方法

    ② 针对于返回值类型的方法,使用return 返回所要的数据。

  • 注意点:return关键字后面不可以声明执行语句。


方法重载

  • 定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。

    • 总结:"两同一不同":同一个类、相同方法名
      参数列表不同:参数个数不同,参数类型不同
	//如下的4个方法构成了重载
	public void getSum(int i,int j){
		System.out.println("1");
	}
	
	public void getSum(double d1,double d2){
		System.out.println("2");
	}
	
	public void getSum(String s ,int i){
		System.out.println("3");
	}
	
	public void getSum(int i,String s){
		System.out.println("4");
	}

//不构成重载的举例:
//如下的3个方法不能与上述4个方法构成重载
//	public int getSum(int i,int j){
//		return 0;
//	}
	
//	public void getSum(int m,int n){
//		
//	}
	
//	private void getSum(int i,int j){
//		
//	}

可变个数形参

  • 可变个数形参的格式:数据类型 ... 变量名
  • 当调用可变个数形参的方法时,传入的参数个数可以是:0个,1个,2个 ...
  • 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
  • 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载。换句话说,二者不能共存。
  • 可变个数形参在方法的形参中,必须声明在末尾
  • 可变个数形参在方法的形参中,最多只能声明一个可变形参。
    public void show(int i){

    }
	
	public void show(String s){
		System.out.println("show(String)");
	}
	
	public void show(String ... strs){
		System.out.println("show(String ... strs)");
		
		for(int i = 0;i < strs.length;i++){
			System.out.println(strs[i]);
		}
	}
//不能与上一个方法同时存在
//	public void show(String[] strs){
//		
//	}


//调用时:
    test.show("hello");
    test.show("hello","world");
    test.show();

    test.show(new String[]{"AA","BB","CC"});

Java中参数传递机制:值传递

  • 如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值。
  • 如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值。

面向对象的特征之----封装

为什么要封装?

我们程序设计追求“高内聚,低耦合

高内聚 :类的内部数据操作细节自己完成,不允许外部干涉

低耦合 :仅对外暴露少量的方法用于使用

隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想


封装性思想具体的代码体现

体现一:将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值

private double radius;

public void setRadius(double radius){
	this.radius = radius;
}

public double getRadius(){
	return radius;
}

体现二:不对外暴露的私有的方法

体现三:单例模式(将构造器私有化)

体现四:如果不希望类在包外被调用,可以将类设置为缺省的。


四种权限修饰符

修饰符 类内部 同一个包 不同包的子类 同一个工程
private YES
缺省 YES YES
protected YES YES YES
public YES YES YES YES
  • 4种权限都可以用来修饰类的内部结构:属性方法构造器内部类
  • 修饰类的话,只能使用:缺省public

类的结构之----构造器

构造器的作用

  • 创建对象
  • 初始化对象的信息

说明:

  • 如果没显式的定义类的构造器的话,则系统默认提供一个空参的构造器
  • 定义构造器的格式:权限修饰符 类名(形参列表){}
  • 一个类中定义的多个构造器,彼此构成重载
  • 一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器
  • 一个类中,至少会有一个构造器。
//构造器
	public Person(){
		System.out.println("Person().....");
	}
	
	public Person(String n){
		name = n;
		
	}
	
	public Person(String n,int a){
		name = n;
		age = a;
	}

属性赋值顺序

① 默认初始化

② 显式初始化

③ 构造器中初始化

④ 通过"对象.方法" 或 "对象.属性"的方式,赋值

以上操作的先后顺序:① - ② - ③ - ④


关键字----this

  • 可以调用的结构:属性、方法;构造器
  • this调用属性、方法:
  • this理解为:当前对象 或 当前正在创建的对象

在类的方法中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象属性或方法。但是,通常情况下,我们都择省略"this."。特殊情况下,如果方法的形参和类的属性同名时,我们必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参。

在类的构造器中,我们可以使用"this.属性"或"this.方法"的方式,调用当前正在创建的对象属性或方法。但是,通常情况下,我们都择省略"this."。特殊情况下,如果构造器的形参和类的属性同名时,我们必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参。


  • this调用构造器:

    ① 我们在类的构造器中,可以显式的使用"this(形参列表)"方式,调用本类中指定的其他构造器
    ② 构造器中不能通过"this(形参列表)"方式调用自己
    ③ 如果一个类中有n个构造器,则最多有 n - 1构造器中使用了"this(形参列表)"
    ④ 规定:"this(形参列表)"必须声明在当前构造器的首行
    ⑤ 构造器内部,最多只能声明一个"this(形参列表)",用来调用其他的构造器


面向对象的特征之----继承

  • 为什么要有类的继承性?

    ① 减少了代码的冗余,提高了代码的复用性

    ② 便于功能的扩展

    ③ 为之后多态性的使用,提供了前提


  • 子类继承父类以后有哪些不同?

    一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。

    特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私的结构。只因为封装性的影响,使得子类不能直接调用父类的结构而已。

    子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。


  • Java中继承性的说明

    一个类可以被多个子类继承。

    Java中类的单继承性:一个类只能有一个父类

    子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类

    子类继承父类以后,就获取了直接父类以及所间接父类中声明的属性和方法


  • Object类的理解

    如果我们没显式的声明一个类的父类的话,则此类继承于Object类

    所的java类(除Object类之外都直接或间接的继承于Object类

    意味着,所的java类具有Object类声明的功能。


方法重写

  • 什么是方法的重写(overrideoverwrite)?

    子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作

    重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。


  • 重写的规则

    子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同

    子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符

    子类不能重写父类中声明为private权限的方法

    父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void

    父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类

    父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double)

    子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型


  • 注意:高频面试题方法重写和重载

关键字----super

  • super 关键字可以理解为:父类的

  • 可以用来调用的结构:属性、方法、构造器

  • super调用属性、方法

    我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."

    特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性。

    特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。


  • super调用构造器:

    我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器

    "super(形参列表)"的使用,必须声明在子类构造器的首行!

    我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二一,不能同时出现

    在构造器的首行,没显式的声明"this(形参列表)"或"super(形参列表)",则默认调用的是父类中空参的构造器:super()

    在类的多个构造器中,至少一个类的构造器中使用了"super(形参列表)",调用父类中的构造器


  • 注意:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。

面向对象的特征之----多态

  • 多态性的理解

    可以理解为一个事物的多种形态。


  • 何为多态性

    对象的多态性:父类的引用指向子类的对象


  • 多态性的使用

    虚拟方法调用:有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。

    总结:编译,看左边;运行,看右边


  • 多态性的使用前提

    ① 类的继承关系

    ② 方法的重写


  • 多态性使用的注意点

    对象的多态性,只适用于方法,不适用于属性


  • 关于向上转型与向下转型

    向上转型:就是多态


    向下转型:

    • 为什么使用向下转型?

      有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。如何才能调用子类特的属性和方法?使用向下转型。


    • 如何实现向下转型?

      使用强制类型转换符:()


    • 使用时的注意点

      ① 使用强转时,可能出现ClassCastException的异常。
      ② 为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。


    • instanceof的使用:

      ① a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
      ② 如果 a instanceofA返回true,则 a instanceof B也返回true.其中,类B是类A的父类。

      要求a所属的类与类A必须是子类和父类的关系,否则编译错误。


  • 面试题:谈谈你对多态性的理解?

    ① 实现代码的通用性。
    ② Object类中定义的public boolean equals(Object obj){ }
    JDBC:使用java程序操作(获取数据库连接、CRUD)数据库(MySQL、Oracle、DB2、SQL Server)
    ③ 抽象类、接口的使用肯定体现了多态性。(抽象类、接口不能实例化)


  • 多态是编译时行为还是运行时行为?


Object类的使用

java.lang.Object类的说明

  • 1.Object类是所Java类的根父类
  • 2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object
  • 3.Object类中的功能(属性、方法)就具通用性。
  • 属性:无
  • 方法:equals()toString()getClass()hashCode()clone()finalize()wait()notify()notifyAll()
  • Object类只声明了一个空参的构造器

equals()方法

是一个方法,而非运算符

  • 只能适用于引用数据类型
  • Object类中equals()的定义:
  • public boolean equals(Object obj) {
    return (this == obj);
    }
  • 说明:Object类中定义的equals()==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
  • StringDateFile包装类等都重写了Object类中的equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同。
  • 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们就需要对Object类中的equals()进行重写.
  • 重写的原则:比较两个对象的实体内容是否相同.

== 运算符的使用

  • 可以使用在基本数据类型变量和引用数据类型变量中
  • 如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
  • 如果比较的是引用数据类型变量:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
  • 补充: == 符号使用时,必须保证符号左右两边的变量类型一致。

toString()方法

当我们输出一个对象的引用时,实际上就是调用当前对象的toString()

  • Object类中toString()的定义:
  • public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
  • StringDateFile包装类等都重写了Object类中的toString()方法。使得在调用对象的toString()时,返回"实体内容"信息
  • 自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容"

包装类

  • 为什么要有包装类?

    为了使基本数据类型的变量具有类的特征,引入包装类。

  • 基本数据类型与对应的包装类

    基本数据类型 包装类
    byte Byte
    short Short
    int Integer
    long Long
    float Float
    double Double
    boolean Boolean
    char Character

  • 需要掌握的类型间的转换

    基本数据类型<--->包装类:JDK 5.0 新特性:自动装箱 与自动拆箱
    基本数据类型、包装类--->String调用String重载的valueOf(Xxx xxx)
    String--->基本数据类型、包装类:调用包装类的parseXxx(String s)
    注意:转换时,可能会报NumberFormatException


关键字 static

  • 可以用来修饰的结构

    属性方法代码块内部类


  • static修饰属性

    static修饰的属性我们称为静态变量(或类变量)

    非静态变量:我们创建了类的多个对象,每个对象都独立的拥一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改

    静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的


  • static修饰属性的其他说明:

    静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用

    静态变量的加载要早于对象的创建

    由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中


  • static修饰方法:静态方法、类方法

    随着类的加载而加载,可以通过"类.静态方法"的方式进行调用

    静态方法中,只能调用静态的方法或属性

    非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性


  • static的注意点:

    在静态的方法内,不能使用this关键字、super关键字

    关于静态属性和静态方法的使用,大家都从生命周期的角度去理解。


  • 如何判定属性和方法应该使用static关键字:

    关于属性

    属性是可以被多个对象所共享的,不会随着对象的不同而不同的。

    类中的常量也常常声明为static


    关于方法

    操作静态属性的方法,通常设置为static

    工具类中的方法,习惯上声明为static的。 比如:MathArraysCollections


浅谈设计模式

  • 什么是设计模式?

    设计模式是在大量的实践中总结和理论化之后优的代码结构、编程风格、以及解决问题的思考方式


  • 常用设计模式

    23种经典的设计模式

    • 创建型模式(5种)

      工厂方法模式抽象工厂模式单例模式建造者模式、原型模式。


    • 结构型模式(7种)

      适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式


    • 行为型模式(11种)

      策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。


单例模式

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例


  • 具体代码的实现

    //饿汉式1:
    class Bank{
    	
    	//1.私化类的构造器
    	private Bank(){
    		
    	}
    	
    	//2.内部创建类的对象
    	//要求此对象也必须声明为静态的
    	private static Bank instance = new Bank();
    	
    	//3.提供公共的静态的方法,返回类的对象
    	public static Bank getInstance(){
    		return instance;
    	}
    }
    
    //饿汉式2:使用了静态代码块
    class Order{
    	
    	//1.私化类的构造器
    	private Order(){
    		
    	}
    	
    	//2.声明当前类对象,没初始化
    	//此对象也必须声明为static的
    	private static Order instance = null;
    
    	static{
    		instance = new Order();
    	}
    	
    	//3.声明public、static的返回当前类对象的方法
    	public static Order getInstance(){
    		return instance;
    	}
    	
    }
    
    //懒汉式:
    class Order{
    	
    	//1.私化类的构造器
    	private Order(){
    		
    	}
    	
    	//2.声明当前类对象,没初始化
    	//此对象也必须声明为static的
    	private static Order instance = null;
    	
    	//3.声明public、static的返回当前类对象的方法
    	public static Order getInstance(){
    		
    		if(instance == null){
    			
    			instance = new Order();
    			
    		}
    		return instance;
    	}
    	
    }
    

  • 两种方式的对比
    • 饿汉式:
      • 坏处:对象加载时间过长。
      • 好处:饿汉式是线程安全的
    • 懒汉式:好处:延迟对象的创建。
      • 目前的写法坏处:线程不安全。--->到多线程内容时,再修改

类的结构之 ---- 代码块

代码块的重要性较属性、方法、构造器差一些


  • 代码块的作用

    用来初始化类、对象的信息


  • 代码块要是使用修饰符,只能使用static


  • 分类:静态代码块 vs 非静态代码块

    • 静态代码块

      内部可以输出语句
      随着类的加载而执行,而且只执行一次
      作用:初始化类的信息
      如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
      静态代码块的执行要优先于非静态代码块的执行
      静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构

    • 非静态代码块

      内部可以输出语句
      随着对象的创建而执行
      每创建一个对象,就执行一次非静态代码块
      作用:可以在创建对象时,对对象的属性等进行初始化
      如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
      非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法


关键字 ---- final

  • 可以用来修饰:类、方法、变量

  • 具体的:

    • final 用来修饰一个类:此类不能被其他类所继承。

      例如:String类、System类、StringBuffer


    • final 用来修饰方法:表明此方法不可以被重写

      例如:Object类中getClass()


    • final 用来修饰变量:此时的"变量"就称为是一个常量

      •   final修饰属性:可以考虑赋值的位置:显式初始化、代码块中初始化、构造器中初始化
        
      •   final修饰局部变量:
        
      •   尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
        

    • static final用来修饰属性:全局常量


关键字 ---- abstract

abstract: 抽象的

  • 可以用来修饰:方法

    • abstract修饰类:抽象类

      此类不能实例化

      抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)

      开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作 --->抽象的使用前提:继承


    • abstract修饰方法:抽象方法

      抽象方法只方法的声明,没方法体

      包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。

      若子类重写了父类中的所的抽象方法后,此子类方可实例化

      若子类没重写父类中的所的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰


  • 注意

    • abstract不能用来修饰:属性、构造器等结构
    • abstract不能用来修饰私有方法、静态方法、final的方法、final的类

关键字 ---- interface

interface:接口

  • 使用说明
    • Java中,接口和类是并列的两个结构
    • 接口中不能定义构造器的!意味着接口不可以实例化
    • Java开发中,接口通过让类去实现implements的方式来使用
    • 如果实现类覆盖了接口中的所抽象方法,则此实现类就可以实例化
    • 如果实现类没覆盖接口中所的抽象方法,则此实现类仍为一个抽象类
    • Java类可以实现多个接口 --->弥补了Java单继承性的局限性
    • 接口与接口之间可以继承,而且可以多继承
    • 接口的具体使用,体现多态性
    • 接口,实际上可以看做是一种规范

  • 体会面向接口编程

类的结构之 ---- 内部类

这部分了解即可,实际用的很少


  • 内部类定义:Java中允许将一个类A声明在另一个类B中,则类A就是内部类


  • 内部类的分类

    • 成员内部类(静态、非静态 )
    • 局部内部类(方法内、代码块内、构造器内)

  • 成员内部类的理解

    • 一方面,作为外部类的成员

      调用外部类的结构

      可以被static修饰

      可以被4种不同的权限修饰

    • 另一方面,作为一个类

      类内可以定义属性、方法、构造器等

      可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承

      可以被abstract修饰


  • 如何创建成员内部类的对象?(静态的,非静态的)

    //创建静态的Dog内部类的实例(静态的成员内部类):
    Person.Dog dog = new Person.Dog();
    
    //创建非静态的Bird内部类的实例(非静态的成员内部类):
    //Person.Bird bird = new Person.Bird();//错误的
    Person p = new Person();
    Person.Bird bird = p.new Bird();
    

  • 如何在成员内部类中调用外部类的结构?

    class Person{
    	String name = "小明";
        public void eat(){
        }
    	//非静态成员内部类
    	class Bird{
    		String name = "杜鹃";
    		public void display(String name){
    			System.out.println(name);//方法的形参
    			System.out.println(this.name);//内部类的属性
    			System.out.println(Person.this.name);//外部类的属性
    		//Person.this.eat();
    		}
    	}
    }
    

  • 局部内部类的使用

//返回一个实现了Comparable接口的类的对象
	public Comparable getComparable(){
		
		//创建一个实现了Comparable接口的类:局部内部类
		//方式一:
//		class MyComparable implements Comparable{
//
//			@Override
//			public int compareTo(Object o) {
//				return 0;
//			}
//			
//		}
//		
//		return new MyComparable();
		
		//方式二:
		return new Comparable(){

			@Override
			public int compareTo(Object o) {
				return 0;
			}
			
		};
		
	}

  • 注意点

    在局部内部类的方法中(比如:show如果调用局部内部类所声明的方法(比如:method)中的局部变量(比如:num)的话,要求此局部变量声明为final的。

    jdk 7及之前版本:要求此局部变量显式的声明为final

    jdk 8及之后的版本:可以省略final的声明

    成员内部类和局部内部类,在编译以后,都会生成字节码文件


异常处理


异常的体系结构

  • java.lang.Throwable

    • java.lang.Error一般不编写针对性的代码进行处理

    • java.lang.Exception可以进行异常的处理

      • 编译时异常(checked)

        • IOException
          • FileNotFoundException
        • ClassNotFoundException
      • 运行时异常(unchecked,RuntimeException)

        • NullPointerException
        • ArrayIndexOutOfBoundsException
        • ClassCastException
        • NumberFormatException
        • InputMismatchException
        • ArithmeticException

从程序执行过程,看编译时异常和运行时异常

  • 编译时异常:执行javac.exe命名时,可能出现的异常
  • 运行时异常:执行java.exe命名时,出现的异常

异常的处理

  • java异常处理的抓抛模型

    • 过程一:"抛"

      程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,并将此对象抛出,一旦抛出对象以后,其后的代码就不再执行


      关于异常对象的产生:

      ① 系统自动生成的异常对象

      ② 手动的生成一个异常对象,并抛出throw


    • 过程二:"抓":可以理解为异常的处理方式

      try-catch-finally

      throws


  • 异常处理方式一try-catch-finally

    try{
     	//可能出现异常的代码
      }catch(异常类型1 变量名1){
      		//处理异常的方式1
      }catch(异常类型2 变量名2){
      		//处理异常的方式2
      }catch(异常类型3 变量名3){
     		//处理异常的方式3
      }
     ....
     finally{
     		//一定会执行的代码
     }
    

    说明:

    1. finally是可选的

    2. 使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配

    3. 一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没写finally的情况。继续执行其后的代码)

    4. catch中的异常类型如果没子父类关系,则谁声明在上,谁声明在下无所谓。

      catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错

    5. 常用的异常对象处理的方式:

      String getMessage()

      printStackTrace()

    6. try结构中声明的变量,再出了try结构以后,就不能再被调用

    7. try-catch-finally结构可以嵌套


  • 如何看待代码中的编译时异常和运行时异常?

    • 使用try-catch-finally处理编译时异常,是得程序在编译时就不再报错,但是运行时仍可能报错。相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。
    • 开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。针对于编译时异常,我们说一定要考虑异常的处理。

  • 异常处理方式二

    throws + 异常类型写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
    一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行!


  • 对比两种处理方式

    try-catch-finally:真正的将异常给处理掉了。
    throws的方式只是将异常抛给了方法的调用者。并没真正将异常处理掉。


手动抛出异常对象

  • 在程序执行中,除了自动抛出异常对象的情况之外,我们还可以手动的throw一个异常类的对象。

  • throw throws区别

    throw 表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内。
    throws 属于异常处理的一种方式,声明在方法的声明处。


  • 示例
class Student{
	
	private int id;
	
	public void regist(int id) throws Exception {
		if(id > 0){
			this.id = id;
		}else{
			//手动抛出异常对象
//			throw new RuntimeException("您输入的数据非法!");
//			throw new Exception("您输入的数据非法!");
			throw new MyException("不能输入负数");

		}
		
	}

	@Override
	public String toString() {
		return "Student [id=" + id + "]";
	}
	
	
}

自定义异常类

  • 如何自定义一个异常类?
    1. 继承于现的异常结构:RuntimeExceptionException
    2. 提供全局常量:serialVersionUID
    3. 提供重载的构造器

public class MyException extends Exception{
	
	static final long serialVersionUID = -7034897193246939L;
	
	public MyException(){
		
	}
	
	public MyException(String msg){
		super(msg);
	}
}
posted @ 2020-11-22 18:59  一只小白而已  阅读(82)  评论(0)    收藏  举报