1.什么是编程语言?
开发于sun公司,现属oracle
人与计算机沟通交流的一种特殊语言
詹姆斯·高斯林 java语言创始者
配置环境变量:
jdk java开发工具包
jre java运行时环境
jvm java虚拟机
java程序执行过程:
1.先编写java源代码 javac.exe
2.对java源代码进行编译 javac 源文件名称
编译完成后会生成当前源文件所生成的字节码文件.class
字节码文件是开发人员看不懂的, 只有jvm虚拟机才能识别
3.运行java代码 java.exe
java 类名
java语言是支持跨平台的:用java编写的源代码可以在不同的系统 上运行
使用JDK开发完成的java程序,交给JRE去运行
java语法规范
1.类和接口要声名一个标识符用大驼峰命名法
2.变量和方法要声名标识符用小驼峰命名法
注释:
编译器中按住alt可以选择一列
#快捷键 ctrl + shift + /
单行注释://注释文字
多行注释:/* 注释文字 */
文档注释:/** 注释文字 */
61class Hello{2 public static void main(String[] args){3 System.out.println("Hello world");4 }5}6//java最基本的Hello world
在程序执行的过程中其值不可以发生改变
在方法外面定义,全大写,单词与单词之间用下划线分隔开
java是一门强类型语言,对于每一种数据都定义了明确的具体数据类型
在内存总分配了不同大小的内存空间
数据类型
1.基本数据类型:
a.数值型:整数类型(byte,short,int,long)
浮点类型(float,double)
b.字符型:(char)
c.布尔型:(boolean)
2.引用数据类型:
a.类: (class)
b.接口:(interface)
c.数组: ([ ])
101double d = 3.14;2
3float f = 3.14f;4
5char c = 'q';6
7String s = "hello world";8//String是引用数据类型9
10boolean b = false;有几个数据类型关键字就开辟几份内存空间
强制转换:
21double d =3.14;2int i = (int)d;运算符+ - * / %(取余)
191num++;2//后置++,先取值再赋值3--num;4//前置--,先赋值再取值5d1 += d2 -> d1 = d1 + d2;6"Hello" instanceof String //结果true,检查是否是类的对象7 8 //与运算 两者同时为真结果为真9 if (1 > 2 & 1 == 2);10 //不短路 false & false11 if (1 > 2 && 1 == 2);12 //短路运算 false && 不运算13
14//或运算 两者只要有一者为真,结果为真15if (1 < 2 | 2 < 3);16if (1 <2 || 2 < 3);17//非运算,取反18(1 > 2) -> false !(1 > 2) -> true19 成员变量:实例变量,静态变量
在JVM虚拟机的栈内存开辟空间,但静态变量在方法区空间开辟内存
局部变量:方法体内部的变量(包括参数中的变量)
JAVA对类的定义:
java本身是面向对象的语言,如果需要使用java去开发一个软件**
那么,软件中的每一个模块都是一个个体,每一个个体会拆分为很多单元,每个单元本身
是一个模糊的概念,针对这样的一个概念,我们一般用类去定义,然后在具体执行的时候,
需要让模块之间相互协作,正常的去模拟现实生活中场景的时候,就需要创建这个类
所对应的实例
将鼠标光标放置于未被导入的包名称上,按住alt + Enter 可以快速import包 (IntelliJ IDEA支持)
创建类
new关键字 创建类的实例
创建类实例的对象
对象.调用方法
方法是完成特定功能的代码块
格式:
721 修饰符 返回值类型 方法名(参数类型 参数名1, 参数类型 参数名2..){2 函数体;3 return 返回值;4}5
6
7 8package com.tly.test;9
10/**11 * 第一个简单java小程序,输入三个浮点数,求最小值 12 */13
14import java.util.Scanner;15
16public class Test02 {17 public static void main(String[] args) {18 19 //创建Test02的对象20 Test02 getMinNum = new Test02();21 22 //接收输入的浮点数据23 Scanner scanner = new Scanner(System.in);24
25 System.out.println("请输入第一个浮点数据类型:");26 27 //创建一个浮点型变量接收返回的参数28 double num1 = scanner.nextDouble();29
30 System.out.println("请输入第二个浮点数据类型:");31
32 double num2 = scanner.nextDouble();33
34 System.out.println("请输入第三个浮点数据类型:");35
36 double num3 = scanner.nextDouble();37 38 //调用对象的方法并传输传输39 double numMin = getMinNum.getMinNum(num1, num2, num3);40
41 System.out.println("最小值为:"+numMin);42
43 }44 45 //创建类的getMinNum方法46 public double getMinNum(double num1,double num2, double num3){47
48 double[] doubles = new double[3];49
50 //将数据传输给列表51 doubles[0] = num1;52 doubles[1] = num2;53 doubles[2] = num3;54
55 //创建中间值56 double numMin = num1;57
58 for (int i = 0; i < doubles.length; i++) {59
60 if(doubles[i] < numMin){61
62 numMin = doubles[i];63
64 }65 66 }67
68 return numMin;69 }70}71
72
方法特性:
递归:
使用递归会严重损耗系统资源,因此要尽量避免使用递归
方法的重载:
在一个区间内,java语法中运行方法名称相同,参数列表不同的语法格式,这种现象我们称之为重载
在参数中的位置只能出现在最后 格式:数据类型...
可变长参数本质其实就是一个数组
可变长参数数值可以传也可以不传,也可以传多个
new创建的容器存放在堆内存中
数组是一个引用数据类型
可以通过new的方式去创建数组
在不赋值的情况下,取到的是默认值,整型默认值是0
261//创建数组2int[] ints = new int[10];3
4//给数组赋值5ints[0] = 100;6
7//遍历数组8for (int i = 0; i < ints.length; i++){9 10 System.out.println(ints[i]);11
12}13
14//创建字符串数组15String[] strings = new String[5];16
17//赋值18String[0] = "hello";19
20//foreach遍历21//for (数据的数据类型 遍历的每一项 : 容器对象)22for (String str : strings){23 24 System.out.println(str);25 26}数组操作常见的小问题:
91数组索引越界2
3 -ArrayIndexOutOfBoundsException4
5 -访问到了数组中的不存在的索引时发生。6
7空指针异常8 -NullPointerException9 -数组引用没有指向实体,却在操作实体中的元素时。
字符串常用函数
561//定义字符串2String str = "hello world";3
4//str.equals() 判断字符是否完全匹配 5System.out.println(str.equals("hello world"));6//输出true7
8//str.contains()判断是否包含某一字符串9System.out.println(str.contains("hello"));10//输出true11
12//str.substring()切割字符串 int beginIndex13System.out.println(str.substring(6));14//输出world 切割[0,6)15
16System.out.println(str.substring(6,11));17//输出world 切割(6,11]18
19//str.toLowerCase()将大写字母转成小写20System.out.println(str.toLowerCase());21
22//str.length() 获取字符串字符个数23System.out.println(str.length());24//输出1125
26//str.endsWith()判断字符串是否以某个字符串结尾27System.out.println(str.endsWith("d"));28//输出true29
30//str.split()字符串的分割31String[] strings = str.split(" ");32
33//遍历数组34for (String s:strings){35 36 System.out.println(s);37 38}39//输出hello 40//输出world41
42//将字符串转为字符数组43char[] chars = str.toCharArray();44
45for (String c:chars){46 47 System.out.println(c);48 49}50
51//str.replace()字符串替换方法52String str4 = str.replace("world","java");53System.out.println(str4);54//输出hello java55
56
以后我们开发项目的时候,要不断的去声明类,在类中需要去封装属性和行为,
但是针对某一些属性,我们需要将它保护起来,不希望直接对外界提供访问。
所以我们会将这些属性用private关键字进行修饰,private代表的是私有的访问权限修饰符。
被private修饰的属性只能在本类中才能访问到
alt + INS键快速选择创建类的get方法与set方法
xxxxxxxxxx261public class User {2
3 private String userName;4
5 private String address;6
7 private String idCard;8
9 private String phoneID;10 11 //提供一个idCard对应的get方法,没有参数,但是有返回值12 public String getIdCard(){13
14 return idCard;15
16 }17
18 //提供一个idCard对应的set方法,有参数,但是没有返回值19 public void setIdCard(String idCard){20
21 this.idCard = idCard;22
23 }24
25
26}
栈内存:主函数,定义的普通函数,基本数据类型,数据地址(引用数据类型的地址),所有的方法
堆内存:new出来的对象,应用数据类型的值
方法区内存:方法区内存中一般存放的是代码片段(java源文件编译之后的字节码文件),以及常量池
静态方法存在栈内存中,静态变量存在于方法区内存中
定义在类中的实例变量一定是在堆内存中开辟的空间,因为new出来的部分一定存在于堆内存中。
在JVM内存中,所有的方法,无论是静态方法还是普通方法(实例方法),都是在栈中分配空间。
局部变量是存在于方法体内部的变量(包含了参数),局部变量如果是基本数据类型,则直接在栈中开辟空间,如果是引用数据类型,变量声明部分在栈中,实例创建部分在堆中,如果是实例遍历,所有的内存都会在堆中去开辟。
常量池一般存储字符串常量
Java中通过 == ,如果比较的是基本数据类型,比的是值,如果比较的是引用数据类型,则比较的是内存地址
是一个权限修饰符
可以修饰成员(成员变量和成员方法)
被private修饰的成员只能在本类中才能访问
this:代表所在类的对象的引用(方法被哪个对象调用,this就代表那个对象)
this.变量名 访问的是本类中的实例变量
作用:给对象的数据进行初始化
方法名与类名相同,没有返回值类型,连void也没,没有具体的返回值
如果不提供构造方法,那么系统会给出默认的构造方法,如果提供了构造方法(不管是否带参数),系统将不再提供默认无参数的构造方法
构造方法可以重载
构造方法的作用:
无参构造:实例化对象,但是实例化对象后,所有的属性都是没有被赋值的
有参构造:实例化对象,可以在创建对象时,就给实例的属性赋值
可以修饰方法,可以修饰属性,但是不能修饰类
被静态关键字修饰的属性和方法,会成为一个类级别的元素,会随着类的加载而加载
局部变量不能被static修饰
被static修饰的变量我们称之为静态变量
静态变量会随着类的加载而加载,在JVM解析字节码文件时,该变量的内存空间就会被开辟出来
静态变量的内存空间会被开辟在该方法区中
静态方法:
xxxxxxxxxx311public class StaticTest01{2 3 private static String name; //静态变量4 5 public static void main(String[] args){6 7 //调用静态方法8 StaticTest01.getMethod();9 10 StaticTest01 staticTest01 = new StaticTest01();11 12 //调用实例方法13 staticTest01.getMethod02();14 }15 16 //声明一个静态方法17 public static void getMethod(){18 19 System.out.println("我是一个静态方法!");20 21 }22 23 public void getMethod02(){24 25 System.out.println("我是一个静态方法!");26 27 } 28 29}30
31
在类加载的时候将会被执行的语句体,而且只加载一次(在主函数执行前执行)
xxxxxxxxxx51static{2 3 System.out.println("static 执行了!!");4 5}
getXxx()setXxx()setXxx()
概述:多个类中存在相同的属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
通过extends关键词可以实现类与类的继承
class 子类名 extends 父类名 {}
单独的这个类称为父类,基类,或者超类;这多个类可以称为子类或者派生类
###
继承可以让子类去继承父类,这样子类会继承父类所有的属性和方法,在java中是不允许有多继承的
Object类:是java中的祖宗类,当一个类在代码层面没有去继承任何的父类,那么这个类
默认会继承Object类
继承的重写(覆盖):
子类继承父类以后,可以获取到父类所有的方法(包含被private修饰的私有方法,但是访问不到)
如果父类方法的功能不能够满足子类的需求,这个时候,子类可以针对这个方法进行重写
父类中私有方法不能被重写
子类重写父类方法时,访问权限不能更低
父类静态方法,子类也必须通过静态方法进行重写
提高了代码的复用性
提高了代码的维护性
让类与类之间产生了关系,是多态的前提
不要为了部分功能而去继承 ,继承中类之间的体现是“is a”的关系
继承中成员变量的关系
在子类方法中访问一个变量:
子类继承父类之后,子类可以通过super关键字去指向父类的元素(属性,普通方法,构造方法)
如果指向父类的构造用super() 如果指向子类的构造用 this()
xxxxxxxxxx281public class Student extends Person{2 3 public Student(){4 5 //指向的是父类的无参构造,不写则默认存在6 super();7 8 }9 10 11 public void sleep(){12 13 //指向父类的普通方法,只能存在于子类构造中 14 super.sleep();15 16 }17 18 public void getMethod(){19 20 //this指向当前类实例的属性21 System.out.println(this.name);22 23 //super指向父类的属性24 System.out.println(super.name);25 26 }27 28}
子类中所有的构造方法默认都会访问父类中的空参数的构造方法
因为子类会继承父类中的数据,可能还会使用父类的数据
所以,子类初始化之前,一定要先完成父类数据的初始化
每一个构造方法的第一条语句默认都是:super()
方法重写与方法重载的区别?方法重载能改变返回值类型吗?
答:方法重写建立在类的继承关系上,是对父类同名方法的覆盖。方法重载是一个类中,多个同名方法,但是参数列表不同。方法重载可以改变返回值类型。
某一事物,在不同时刻表现出来的不同状态
多态是面向对象特征之一,也是最重要的一个特征
多态思想:
为了解除类与类之间的耦合(模块与模块之间的耦合),不能面向具体编程,而要面向抽象编程(面向接口编程)
首先多态必须要有父子关系的体现(继承,实现),是在不同时刻展现出不同的状态
父类的引用接收子类对象(向上转型),或者是向下转型都是多态的体现
多态在程序执行时分为编译阶段和运行阶段:
普通方法:
编译阶段看左边:编译时要检查调用的方法在父类中是否存在
运行阶段看右边:因为我们实际new出来的对象是子类对象,所以最终执行的方法也是子类的方法
静态方法:
静态方法可以用类名去调用,类名.静态方法名()
静态方法也可以用实例去调用,对象引用.静态方法名(),本质也是通过类名去调用
静态方法是一个类级别的方法,所以它已经脱离了实例,和对象没有关系
编译看左边,运行看左边
xxxxxxxxxx331class PloyTest01{2 3 public static void main(String[] args){4 5 Cat cat = new Cat();6 7 //通过多态机制来创建猫的实例8 Animal animal = new Cat();9 10 animal.eat();11 12 //引用数据类型的强转就是多态的一种,向上转型13 Animal animal1 = (Animal)cat;14 15 //面试题:16 Animal animal = new Rabbit();17 18 //判断animal1是否属于Cat,属于则进行强转19 if (animal1 instanceof Cat){20 21 //向下转型22 Cat cat1 = (Cat)animal1;23 24 cat1.eat();25 26 }27 28 29 30 31 }32 33}多态前提和体现
有继承关系
有方法重写
有父类引用指向子类对象
多态在开发中的作用
主任喂养宠物的案例:
有一个主人是master喜欢喂养宠物,喂养的宠物有很多种(猫,狗,兔子,鸟),然后随着时间的推 移,主人会喂养越来越多的宠物,针对于这样的场景我们通过java程序去模拟一下(体现出多态)
重载算不算多态?不算多态(没有体现父子关系)
结论:如果按照传统的方式进行开发(不使用多态):会发现程序进行迭代的时候,需要去修改主人类(需要在原有的代码继承上进行修改),这样做违背了设计模式中的开辟原则(OCP 对扩展开发,对修改关闭):在写程序的时候,尽量避免在原有的代码之上进行修改,这样的话修改的部分容易对别的类(或者说别的模块造成影响)
xxxxxxxxxx661public class Master {2
3 //这是一个喂养的方法,用父类的引用去接收子类的对象4 public void feed(Pet pet){5
6 pet.eat();7
8 }9
10}11
12//创建抽象类Pet13public class Pet {14
15 public void eat(){16
17 System.out.println("所有的宠物都有吃的特性");18
19
20 }21
22}23
24
25public class Cat extends Pet{26
27 public void eat(){28
29 System.out.println("猫吃鱼");30
31 }32
33}34
35public class Rabbit extends Pet{36
37 public void eat(){38
39 System.out.println("兔子爱吃萝卜");40
41 }42
43}44
45public class Dog extends Pet{46
47 public void eat(){48
49 System.out.println("狗爱啃骨头");50
51 }52
53}54
55public class MasterTest {56
57 public static void main(String[] args) {58
59
60 Master master = new Master();61
62 master.feed(new Cat());63
64 }65
66}
一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类
抽象类是java中最顶级的一种表现形式,接下来就是类,再接下来就是对象
抽象类用abstract修饰 > 类class > new 对象
我们如果想创建对象的话,只能通过类class去创建,抽象类是不能创建对象的
因为抽象类的下一级是类
格式:abstract class 类名{}
xxxxxxxxxx411public abstract class Animal{2 3 private String name;4 5 private static int age;6 7 //定义抽象方法,抽象方法是没有方法体的8 //为什么抽象类没有方法体?因为抽象方法本身就是模糊的,不用去提供具体的实现9 public abstract String eat();10 11 public void move(){12 13 System.out.println("动物会移动")14 15 }16 17 18}19
20public class Cat extends Animal{21 22 23 public String eat(){24 25 return "猫喜欢吃鱼";26 27 }28 29}30
31public class AbstractTest01{32 33 public static void main(String[] args){34 35 Animal animal = new Cat();36 37 System.out.println(animal.eat());38 39 }40 41}
抽象类中是有构造方法的(抽象类不可以实例化对象,但是却存在构造函数)
抽象类父类构造函数是通过子类构造去调用的,通过super关键字调用父类构造
xxxxxxxxxx101public class Cat extends Animal{2 3 4 public Cat(String name,int age){5 6 super(name,age);7 8 }9 10}
final关键字:可以修饰类,成员变量,局部变量,方法(静态方法,实例方法)
用final关键字能不能修饰抽象类?不可以,因为抽象类的作用就是用来被子类继承的,但是被final修饰的类是无法被子类继承的,final和abstract是不能同时出现的
如何去声明一个常量?
常量是程序运行过程中不可以发生改变的量
对于那些无论创建多少次对象,值都亘古不变的数据,我们把该属性定义为常量
xxxxxxxxxx11public static final 属性类型 常量名称(所有字母都大写,单词之间用_分隔) = 值用static修饰常量是因为想让改常量变成一个类级别的,然后随着类加载,该常量就初始化
避免多次开辟空间,浪费系统资源
用final修饰常量,是因为不希望在程序运行时,该量的值发生变化,final代表最终的
接口:接口是一种规范,在进行程序设计的时候,都是基于接口进行开发
因为在声明方法的时候,我们需要这个方法存在多变性,而且可以随着业务的改变,针对这个接口做具体的实现
构造方法:没有,因为接口主要是扩展功能的,而没有具体存在
接口的定义:
格式: interface 接口名 {}
xxxxxxxxxx211package com.os467.interface_test;2
3//声明一个接口 4public interface MyInterface01{5 6 int I = 100;7 8 Object O = new Object();9 10 static String ADDRESS = "南京"11 //以上都是常量,忽略了public static final,不是变量12 13 public static final String NAME = "Tom";14 //可以省略public static final15 16 //抽象方法17 //public abstract void getMethod();接口中下面的代码等同于这行代码 18 void getMethod();19
20 21}
接口在开发中的作用:
设计模式中提到在设计程序时,我们要面向接口开发,接口+多态,可以实现程序的可拓展性,灵活性
作用:子类实现接口的关键字(类似extends关键字)
接口中定义的抽象方法,在子类实现之后一定要进行重写
格式: class 类名 implements 接口名{}
xxxxxxxxxx241public class MyInterfaceImpl implements MyInterface01{2 3 4 public void getMethod(){5 6 System.out.println("子类实现接口之后重写的方法") 7 8 9 }10 11}12
13public class InterfaceTest01{14 15 public static void main(String[] args){16 17 //通过多态的方式来创建实例18 MyInterface01 myInterface01 = new MyInterfaceImpl();19 20 myInterface01.getMethod();21 22 }23 24}
接口案例
xxxxxxxxxx691package com.os467.interfaceTest.person;2
3public interface Message {4
5 void getMessage();6
7}8--------------------------------------------------------9package com.os467.interfaceTest.person;10
11public class QQ implements Message{12
13 14 public void getMessage() {15 System.out.println("接收的是QQ消息");16 }17}18--------------------------------------------------------19package com.os467.interfaceTest.person;20
21public class Email implements Message{22
23 24 public void getMessage() {25
26 System.out.println("接收的是电子邮件");27 }28}29---------------------------------------------------------30 package com.os467.interfaceTest.person;31
32public class QQ implements Message{33
34 35 public void getMessage() {36 System.out.println("接收的是QQ消息");37 }38}39---------------------------------------------------------40 package com.os467.interfaceTest.person;41
42public class Person {43
44 //接收消息的方法45 public void receive(Message message){46
47 //调用接收消息的方法48 message.getMessage();49
50 }51
52}53----------------------------------------------------------54 package com.os467.interfaceTest.person;55
56public class Test {57
58 public static void main(String[] args) {59
60 Person person = new Person();61
62 //调用接收消息的方法63 person.receive(new QQ());64 person.receive(new WeChat());65 person.receive(new Email());66
67 }68
69}按照多态的方式,由具体的子类实例化,这其实也是多态的一种,接口多态
类对多个接口方法的实现
xxxxxxxxxx131//一个类的多接口实现,接口与接口之间用逗号隔开2public class MyImpl implements MyInterface01, Myinterface02{3 4 public void getMethod(){5 6 }7 8 9 public String getMethod02(){10 11 return null;12 }13}接口与接口之间的多继承
xxxxxxxxxx41//继承接口02,接口01的所有抽象方法与常量2public interface MyInterface03 extends MyInterface02, MyInterface01(){3 4}
总结:抽象类与接口的区别:
抽象类有成员变量,以及抽象方法(没有方法体)与普通方法,拥有自己的构造函数,继承抽象类的子类需要重写抽象类中的所有抽象方法,抽象类没有实例对象,因为其下一级是类
抽象类被继承体现的是”is a“的关系,共性功能
接口可以说是特殊的抽象类,只是接口内只有抽象方法和常量,接口不具有构造函数,实现接口的类需要重写接口中的所有抽象方法,接口没有实例对象,它的作用是用来被子类实现的
接口被实现体现的是:”like a“的关系,扩展功能
类与类:
继承关系,只能单继承,但是可以多层继承
类与接口:
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
接口与接口之间:
继承关系,可以单继承,也可以多继承
接口和抽象类都可以作为一个方法的返回值,参数,也可以作为类的成员变量
xxxxxxxxxx81 public static void main(String[] args) {2
3 public Myinterface02 getFun(){4 5 //返回的是一个接口6 return new MyImpl();7
8 }
package只能存在工程的第一行
包其实就是文件夹
作用:对类进行分类管理
格式:package 包名;
多级包用”.“分开即可
访问权限修饰符区别
| 访问权限修饰符 | 本类 | 同一包下 | 子类(同一包下) | 子类(不同包下) | 不同包下非子类 |
|---|---|---|---|---|---|
| private | √ | × | × | × | × |
| default(不写修饰符) | √ | √ | √ | × | × |
| protected | √ | √ | √ | √ | × |
| public | √ | √ | √ | √ | √ |
把类定义在其他类的内部,这个类就被称为内部类
内部类的访问特点:
内部类可以直接访问外部类的成员,包括私有
外部类要访问内部类的成员,必须创建对象
xxxxxxxxxx51//创建内部类的实例,B是类A的内部类2A.B b = new A().new B();3
4//调用内部类中实例的方法5b.getMethod();内部类中不允许写静态方法
就是内部类的简化写法
前提:存在一个类或接口
这里的类可以是具体类也可以是抽象类
格式:
new 类名或者接口名( ){重写方法;}
本质:
创建一个父类所对应的匿名对象
是一个继承了
类或者实现了接口的子类匿名对象
xxxxxxxxxx151//给接口创建一个匿名对象,可以把 new MyInterface(){}视为接口的匿名子类2MyInterface myInterface = new MyInterface(){3 4 5 public double getSum(double num1, double num2){6 7 return num1 + num2;8 }9 10};11
12double sum = myInterface.getSum(100,200);13
14System.out.println("结果为"+sum);15//结果为 300.0
当程序出现异常时,java会创建对应的异常对象,并且抛出(throw)
当异常抛出之后,会影响接下来代码的执行(打断)
在 Java 中,所有的异常都有一个共同的祖先类Throwable(可抛出)
Throwable有两个子类 Error 和 Exception
错误(Error): JVM系统内部错误,资源耗尽等严重情况
Error是Throwable的子类,用于指示合理的应用程序不应该试图捕获的严重问题
无法从代码层面解决此异常
异常(Exception): 其它因编程错误或偶然的外在因素导致的一般性问题
Exception(异常):是程序本身可以处理的异常,Exception 类有一个重要的子类RuntimeException
常见异常:
RuntimeException
ArithmeticException:数学计算异常
NullPointerException:空指针异常
NegativeArraySizeException:数组索引越界异常
ClassNotFoundException:类文件未找到异常
ClassCastException:造型异常
IOException
FileNotFoundException:文件未找到异常
EOFException:读写文件尾异常
异常:
编译时异常:当JVM进行编译的时候就会出现报错,对于那些大概率会发生的异常,我们会定义成编译时异常,编译时异常要求开发人员必须对其进行处理
直接或间接继承了Exception类的异常就是编译时异常
运行时异常:程序在运行时,才会出现的异常,小概率会出现的异常,我们会定义成运行时异常,开发人员可以不用对其进行处理
直接或者是间接的继承了RuntimeException类,该异常就是运行时异常
1.通过throws关键字将异常往上抛
xxxxxxxxxx91//throws关键字为方法添加方法体内可能出现的异常,如文件找不到异常,不添加则无法运行方法体中的代码2public static void main(String[] args) throws FileNotFoundException {3 4 FileInputStream fileInputStream = new FileInputStream("D:\\test\\test.txt");5
6 System.out.println(fileInputStream);7
8
9 }
2.通过异常的捕获try{}catch{}语句块
语句块机制:
xxxxxxxxxx151try{2 //可能会抛出特定异常的代码段3}catch(MyExceptionType myException){4 //如果myException被抛出,则执行这段代码5}catch(Exception otherException){6 //如果另外的异常otherException被抛出,则执行这段代码7}finally{8 //用于资源回收,回收的资源一般是指会造成对系统资源浪费的对象9 //无条件执行的语句!!一定会执行10} 11/*12通过这种方式去处理异常,在捕获到异常之后,还是一样会创建该异常对象,但是该对象会13被catch语句块所捕获,具体用异常类来接收该异常对象,然后在catch语句块中可以针对14该异常进行相关的处理,程序会继续往下执行15*/注意:用try{…}catch{…}捕捉了异常之后一定要对在catch{…}中对其进行处理,那怕是最简单的一句输出语句,或打印日志来显示整个调用流程
xxxxxxxxxx61catch(Exception e){2 3 //栈输入,打印日志4 e.printStackTrace();5 6}
throws : 是方法可能抛出异常的声明
用在声明方法时,表示该方法可能要抛出异常,交给上层调用它的方法程序处理
通过throws关键字(写在方法后面的)把异常向上抛(抛给该方法的调用处)
不会影响程序正常运行
xxxxxxxxxx111//throws后面为方法中可能出现的异常,如空指针异常2public void getMethod01() throws NullPointerException{3
4}5
6//抛出编译时异常7public FileInputStream getMethod02() throws FileNotFoundException{8 9 return new FileInputStream("D:\\file\\demo.java")10 11}
java给我们提供了很多种异常,但是在做项目的时候,这些异常是不够用的,所以需要我们自定义异常
自定义异常方式:
RuntimeExceptionxxxxxxxxxx211public class ExceptionDemmo extends Exception{2 3 //提供一个构造,把异常信息告诉父类4 public ExceptionDemmo(String message){5
6 super(message);7
8 }9
10}11
12public class RuntimeExceptionDemmo extends RuntimeException{13 14 //提供一个构造,把异常信息告诉父类15 public RuntimeExceptionDemmo(String message){16
17 super(message);18
19 }20
21}系统在遇到异常时会自动生成一个 throw new 异常类(); 来抛出异常
throw是语句抛出一个异常,一般是在方法体的内部,当程序出现某种逻辑错误时由程序员主动抛出某种特定类型的异常
xxxxxxxxxx171public static void main(String[] args) throws Exception{2 3 ExceptionDemo exceptionDemo = new ExceptionDemo("这是我提供的自定义异常信息");4 5 throw exceptionDemo;6 7 //也可写成throw new ExceptionDemmo("这是我提供的自定义异常信息");8}9
10public static void main(String[] args){11 12 RuntimeExceptionDemo runtimeExceptionDemo = new RuntimeExceptionDemo("这是我提供的自定义异常信息");13 14 throw exceptionDemo;15 16 //也可写成throw new RuntimeExceptionDemmo("这是我提供的自定义异常信息");17}
字符串比较的方法:
空指针异常时以后我们写程序经常会遇见的一种异常,接收过来的引用为空,然后拿着该引用去调用方法
空指针异常的解决方案:我们要对接收过来的引用数据类型做非空校验
xxxxxxxxxx191public boolean getEquals(String str1, String str2){2 3 boolean b = false;4 5 if (str1 == null || str == null){6 7 System.out.println("接收的引用为空")8 9 }else{10 11 b = str1.equals(str2);//new NullPointerException();12 13 return b;14 15 }16 17 return b;18 19}
集合:
java中所有的基本数据类型都有对应的封装类(引用数据类型),我们学习的集合是只能存放引用数据类型的容器。
xxxxxxxxxx141int i = 100;2
3//创建一个集合对象4ArrayList arrayList = new ArrayList();5
6//添加元素的方法,自动装箱:int ---> Integer7arrayList.add(i);8
9for (int j = 0; j < arrayList.size(); j++){10 11 //根据下标来取元素12 System.out.println(arrayList.get(j));13 14}Java中有自动装箱机制,会把基本类型转化为其对应的封装类:
将引用数据类型转为基本数据类型就是自动拆箱
Iterator接口:Collection继承的超级接口, 只有一个返回迭代器的方法
Collection : 集合层次中的根接口,JDK没有提供这个接口直接的实现类
将一组对象以集合元素的形式组织到一起,在其子接口中分别实现不同的组织方法
xxxxxxxxxx541//创建一个集合对象2Collection collection = new ArrayList();3
4//添加的是基本数据类型 会自动装箱5collection.add(1000);6
7//添加浮点型 自动装箱8collection.add(3.14);9
10//添加引用数据类型11collection.add(new Object());12
13System.out.println("集合的长度为"+collection.size());14
15//遍历Collection接口方法 遍历集合中的元素16//获取迭代器对象方法17Iterator iterator = collection.iterator();18while (iterator.hasNext()){19 20 Object next = iterator.next();21 22 System.out.println(next);23 24}25
26//判断集合中是否包含指定元素27System.out.println("是否包含元素?"collection.contains(1000));28
29//比较集合是否相等30System.out.println(collection.equals(collection1));31
32//返回哈希码值33System.out.println("hash码值为"+collection.hashCode());34
35//判断集合是否为空36System.out.println("集合是否为空?"+collection.isEmpty());37
38//根据指定元素,删除集合中所对应的元素39collection.remove(1000);40
41//返回集合元素个数42System.out.println("集合元素个数:"+collection.size());43
44//将集合转成数组45Object[] objects = collection.toArray();46
47for (Object object : objects){48 49 System.out.println(object)50 51}52
53//清空集合中的元素54collection.clear();Collection的两个子接口:
是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式
xxxxxxxxxx241public class CollectionTest01 {2
3 public static void main(String[] args) {4
5 List list1 = new ArrayList();6
7 List list2 = new Vector();8
9 list1.add(100);10 list1.add(100);11 list1.add("hello");12 list1.add(new Object());13 list1.add(list2);14 15 //List接口提供按索引访问的方法16 for (int i = 0; i < list1.size(); i++) {17
18 System.out.println(list1.get(i));19
20 }21
22 }23
24}迭代器方法访问元素:
Iterator接口定义了对Collection类型对象中所含元素的遍历等增强处理功能
可以通过Collection接口中定义的iterator()方法
获得一个对应的Iterator(实现类)对象
Set(实现类)对象对应的Iterator仍然是无序的
List(实现类)对象对应的ListIterator对象可以实现对所含元素的双向遍历:
使用next()方法和previous()方法
xxxxxxxxxx191 //获取迭代器对象,注意:获取迭代器对象之后不允许对集合元素进行修改或删除,否则会报错2 Iterator iterator = list1.iterator();3
4 /*遍历集合5 iterator.hasNext()是一个指针,判断集合中下一个6 单元中是否存在元素,如果存在指针向下走一个单元*/7 while(iterator.hasNext()){8
9 //返回当前指针指向的元素10 Object next = iterator.next();11 12 //迭代器的删除方法,删除的是当前遍历的元素13 //iterator.remove();14 15 //最好不要在迭代器中用16 //List.remove(next)17
18 System.out.println(next);19 }forEach循环(加强版for循环):
xxxxxxxxxx51for (Object obj : list1){2
3 System.out.println(obj);4 5 }
案例分析(关于集合当中的方法):
List的方法:
contains 方法: 底层重写了equals方法,判断集合中是否包含某一个元素,如果该元素重写了equals方法,比较的就是内容,如果没有重写equals方法,默认会采用Object类中的equals方法,这时候比较的就是地址
remove方法: 底层重写了equals方法,删除集合中的元素,但是永远只能删除一个元素,因为在底层删除之后就return了
xxxxxxxxxx231/**2 * 创建两个学生对象(属性有 姓名,年龄,身份证号),然后3 * 重写equals方法,在年龄姓名身份证号相同的清空下,两个比较的实例才是相同的!4 *5 */6public class ListDemo {7 public static void main(String[] args) {8
9 //创建集合对象10 List list = new ArrayList();11
12 //创建两个学生实例13 Student student1 = new Student("Tom",20,"1232312312");14 Student student2 = new Student("Tom",20,"1232312312");15
16 list.add(student1);17 18 //因为在contains的底层调用了equals方法,student在contains中用Object类接收19 //而父类Object中的equals方法不能满足需求,因此需要在Student类中去重写equals方法20 System.out.println(list.contains(student2));21 } 22
23}xxxxxxxxxx761public class Student {2
3 private String name;4
5 private int age;6
7 private String idCard;8
9 public Student(String name, int age, String idCard) {10 this.name = name;11 this.age = age;12 this.idCard = idCard;13 }14
15 public Student() {16 }17
18 public String getName() {19 return name;20 }21
22 public void setName(String name) {23 this.name = name;24 }25
26 public int getAge() {27 return age;28 }29
30 public void setAge(int age) {31 this.age = age;32 }33
34 public String getIdCard() {35 return idCard;36 }37
38 public void setIdCard(String idCard) {39 this.idCard = idCard;40 }41
42 //重写equals方法43 44 public boolean equals(Object obj) {45 46 //如果接收到为空 或者 obj不是Student类的对象 返回false47 if (obj == null || !(obj instanceof Student)){48
49 return false;50
51 }52 53 //对obj向下转型,用Student类的对象student接收54 Student student = (Student)obj;55 56 //满足 对象与student的姓名,年龄,身份证号相等 返回true57 if(this.name.equals(student.name) && this.age == student.age && this.idCard.equals(student.idCard)){58
59 return true;60
61 }62 63 //不满足则返回false64 return false;65 }66
67 68 public String toString() {69 return "Student{" +70 "name='" + name + '\'' +71 ", age=" + age +72 ", idCard='" + idCard + '\'' +73 '}';74 }75}76
ArrayList 和 vector 集合区别:
这两个集合底层都封装了数组这一种数据结构,ArrayList是线程不安全的,Vector是线程安全的
所有封装的方法都是一样的,还有就是扩容机制有一点不一样,ArrayList 和 vector最开始的时候只能存放10个元素,默认初始容量是10,当元素超过10个之后,就会触发底层的扩容机制
ArrayList的扩容因子是1.5 10 * 1.5 = 15
Vector的扩容因子是2.0 10 * 2.0 = 20
ArrayList 和 LinkedList 集合区别:
ArrayList 底层是数组,LinkedList 底层是链表结构
数组查询效率高,随机增删效率低
链表随机增删效率高,查询效率低
JDK1.5版本之后引入的新特性,强制让集合只能存放某一种数据类型
<数据类型>钻石表达式
xxxxxxxxxx21//创建只能存放字符串类型的数组 2ArrayList<String> string = new ArrayList<>();
自定义泛型:
xxxxxxxxxx281//<>内部的类容可以是任意的,例如可以是Collection<? extends E> var1,即传入数据类型必须是Collection的子类2//<E>的存在用来接收Person类内部的E的具体引用数据类型3public class Person<E> {4 5 //e这个成员变量的类型为E,E的类型由外部指定6 private E e;7 8 //泛型构造方法形参e的类型也为E,E的类型由外部指定9 public void getE(E e){10
11 System.out.println(e);12
13 }14
15}16
17------------------------------------------------------------18public class Test {19
20 public static void main(String[] args) {21
22 Person<String> stringPerson = new Person<>();23
24 stringPerson.getE("abc");25
26 }27
28}Set接口的实现类:HashSet
底层封装了HashMap
HashSet中不允许出现重复元素,不保证集合中元素的顺序(存入的顺序与取出的不同)
HashSet中允许包含值为null的元素,但最多只能由一个null元素
xxxxxxxxxx131 HashSet<String> hashSet = new HashSet<>();2
3 hashSet.add("abc");4 hashSet.add("efg");5 hashSet.add("xyz");6 hashSet.add("xyz");7
8 for (String s : hashSet) {9
10 System.out.println(s);11
12 }13
Set接口的实现类:TreeSet
底层封装了TreeMap
xxxxxxxxxx141//创建TreeSet集合2TreeSet treeSet = new TreeSet();3
4 treeSet.add(123);5 treeSet.add(124);6 treeSet.add(120);7 treeSet.add(128);8 treeSet.add(129);9
10 for (Object o : treeSet) {11
12 System.out.println(o);13
14 }
map集合是存放键值对的容器
value是key的一个替代品,我们以后操作元素时,是根据key去操作
map集合的key是不允许重复的,如果在添加元素时,key的值重复,那么后添加的元素将会把前一个覆盖掉
value的值可以重复
Map:包含了key-value对, Map不能包含重复的key, SortedMap是一个按照升序排列key的Map
映射(map)是一个存储关键字和值的关联或者说是关键字/值对的对象,给定一个关键字,可以得到它的值
关键字和值都是对象,关键字必须是唯一的,但值是可以被复制的
有些映射可以接收null关键字和null值,而有的则不行
xxxxxxxxxx511//创建集合对象2Map map = new HashMap();3Map map2 = new HashMap();4
5//添加元素6map.put("name","jack");7map.put("age",20);8map.put("address","北京市");9map.put("idCard","123456");10
11//查看集合的长度12System.out.println("集合的长度为"+map.size());13
14//查询集合中是否包含某一个key15System.out.println("是否包含key"+map.containsKey("name"));16
17//是否包含value18System.out.println("是否包含value"+map.containsValue(20));19
20//判断集合对象是否相等21System.out.println(map.equals(map2));22
23//根据key来获取value24System.out.println("name的值为"+map.get("name"));25
26//判断集合是否为空27System.out.println("是否为空"+map.isEmpty);28
29//将map集合中的key部分转成set集合30Set set = map.keySet();31
32for(Object o : set){33 34 System.out.println(o);35 36}37
38//复制集合中所有的元素,将map的内容复制给map239map2.putAll(map);40
41//根据key来删除整个键值,返回的是删除的key所对应的value42Object age = map.remove("age");43
44//将map集合所有的value部分转成collection集合 45Collection collection = map.values();46
47for(Object o : collection){48 49 System.out.println(o);50 51}如何遍历Map集合(两种方法):
1.keySet方法:将map集合所有key部分转成set集合,再根据get()获取对应value的值
xxxxxxxxxx251//创建map集合对象2Map map = new HashMap();3
4//添加元素5map.put("name","Tom");6map.put("age",20);7map.put("address","北京市");8map.put("idCard","123456");9
10//将所有的key转成set集合11Set set = map.keySet();12
13//获取迭代器对象14Iterator iterator = set.iterator();15
16while (iterator.hasNext()){17 18 Object key = iterator.next();19 20 //根据key获取value21 Object value = map.get(key);22 23 System.out.println(key+" = "+value);24 25}2.Set<Map.Entry<K,V> entrySet()
将map集合转成set集合(效率比第一种高)
xxxxxxxxxx141Set<Map.Entry<String,String>> entries = map.entrySet();2
3//这里的Entry<k,V>为Map的内部接口4for (Map.Entry<String,String> entry : entries){5 6 //获取key的值7 String key = entry.getKey();8 9 //获取value的值10 String value = entry.getValue();11 12 System.out.println(key+" = "+value);13 14}Map接口实现类:HashMap:
hashMap集合的存储过程:
hashCode()随机生成哈希码值
我们添加到集合的元素是引用数据类型
那一定会继承Object类中的hashCode方法
首先map集合会调用put()方法来添加元素,
这个时候hashMap底层会生成hash值(如果在该引用数据类型中重写了hashCode方法,那么生成的不再是随机值,数据便会存储在同一单元中)
根据这个hash值来计算该元素的位置应该存放在数组的哪个单元下面
确定了存放的单元,这个时候就会去拿着key的引用
与该单元下所有的node节点进行一个equals比较(该引用数据类型有没有重写equals方法如果重写了,比较的是对应属性的内容,如果没有重写,那比较的就是内存地址)
如果发下equals的返回值是true,新添加进来的节点将会把旧的节点覆盖掉
如果equals的返回值是false,该节点将会存放在数组单元中的单向链表的最下面(成为链表尾节点)
xxxxxxxxxx61//创建集合对象2HashMap<Student,Object> hashMap = new HashMap<>();3
4//添加元素,key和value5hashMap.put(new Student("jack",20),100);6hashMap.put(new Student("jack",20),101);
hashMap案例:
xxxxxxxxxx321//根据hashMap统计字符出现的个数2String str = "djaiwj jdjia1dj dj";3
4 char[] chars = str.toCharArray();5
6 //创建hashMap集合7 HashMap<Character,Integer> hashMap = new HashMap<>();8
9 for (char aChar : chars) {10
11 //在遍历字符数组的过程中判断map集合中是否有该字符,如果有在当前个数基础上加一12 //如果不存在,则把该字符重新添加到集合中,个数赋值为113 if (hashMap.containsKey(aChar)){14
15 //个数在当前基础上加一16 hashMap.put(aChar,hashMap.get(aChar)+1);17
18 }else{19 20 hashMap.put(aChar,1);21
22 }23
24 }25
26 //遍历map集合27 Set<Map.Entry<Character, Integer>> entries = hashMap.entrySet();28
29 for (Map.Entry<Character, Integer> entry : entries) {30
31 System.out.println(entry.getKey()+"===>"+entry.getValue());32 }
Map接口实现类:HashTable:
HashTable和HashMap的区别:
HashMap key-value可以为一次空
HashTable 不可以为空
HashTable是线程同步的,也是线程安全的
其实hashtable就是线程安全版的hashmap,所有的方法以及使用方式都是一样的
hashtable的key和value不允许为空
跟arrayList和vector是一样的,hashmap的执行效率要比hashtable高,因为hashtable所有的方法都添加了线程同步关键字,这样的话在多线程场景下,执行效率是不高,但是以后开发中我们可以通过别的途径来解决线程安全问题,所以hashMap这个集合是我们以后使用的最多key-value集合
xxxxxxxxxx11Hashtable<Object,Object> hashtable = new Hashtable<>();Map接口实现类:TreeMap:
treeMap是一个可以按照key自动排序的集合
xxxxxxxxxx361//创建集合对象2Treemap<Integer,Object> treeMap = new TreeMap<>();3
4treeMap.put(110,101);5treeMap.put(100,101);6treeMap.put(102,101);7
8//map集合转set集合9Set<Map.Entry<String,String>> entries = treeMap.entrySet();10
11//forEach12for (Map.Entry<Integer, Object> entry : entries){13 14 System.out.println(entry.getKey()+" = "+entry.getValue());15 16}17//输出结果:18//100 = 10119//102 = 10120//110 = 10121
22//对自定义的引用数据类型使用treeMap,treeSet需要在类中实现Comparatable接口23public class Goods implements Comparable<Goods> {24 25 private Double price;26 27 //重写compareTo方法,二叉树根据返回的值的正负来存放数据2829public int compareTo(Goods goods){30 31 return (int)(this.price - goods.price);32 33}34 35}36
案例:
用hashMap统计字符出现的个数
xxxxxxxxxx671package com.os467.hashMapDemo;2
3import java.util.HashMap;4import java.util.Map;5import java.util.Set;6
7public class HashMapTest {8
9 public static void main(String[] args) {10
11 //统计每个字符出现的次数12 getCharNum();13
14 }15
16 /**统计字符出现的个数17 *s.matches()正则表达式方法,用于匹配符合规则的字符串18 */19 public static void getCharNum(){20
21 System.out.println("---------统计每个字符出现的次数----------");22
23 //创建HashMap集合24 HashMap<Character,Integer> hashMap = new HashMap<>();25
26 //将字符串切割成字符数组27 char[] chars = getString().toCharArray();28
29 //遍历字符串数组30 for (char c: chars) {31
32 if (hashMap.containsKey(c)) {33
34 //个数在当前基础上加一35 hashMap.put(c, hashMap.get(c) + 1);36
37 } else {38
39 hashMap.put(c, 1);40
41 }42
43 }44 //将map集合转成set集合45 Set<Map.Entry<Character, Integer>> entries = hashMap.entrySet();46
47 //遍历set集合48 for (Map.Entry<Character, Integer> entry : entries) {49
50 System.out.println(entry.getKey()+" ===> "+entry.getValue());51
52 }53
54
55
56
57 }58
59 //获取字符串的方法60 public static String getString(){61
62 return "adaw diicj123";63
64 }65
66}67
Properties集合:
继承了hashtable
xxxxxxxxxx261 //创建集合对象2 Properties properties = new Properties();3
4 //创建流对象5 FileReader fileReader = new FileReader("jdbc");6
7 //通过流对象去解析数据,并把数据放到集合8 properties.load(fileReader);9
10 //私有的方法11 properties.setProperty("username","root");12 properties.setProperty("password","root");13 properties.setProperty("url","jdbc:mysql://localhost:3306");14 properties.setProperty("driver","com.mysql.jdbc.driver");15
16 //取出集合中的数据17 String username = properties.getProperty("username");18 String password = properties.getProperty("password");19 String url = properties.getProperty("url");20 String driver = properties.getProperty("driver");21
22 System.out.println(username);23 System.out.println(password);24 System.out.println(url);25 System.out.println(driver);26
Collection体系:
List接口:
ArrayList:
有序可重复的集合,底层封装了数组这种数据结构,所以有数组所有的特性,可以通过下标索引来获取元素,查询数据的效率会很高,但是随机增删的效率会比较低,本身是一个线程不安全的集合,默认初始容量是10,如果存放元素超过10会触发扩容机制,扩容因子是1.5,每触发一次扩容机制,加载为原来的1.5倍
Vector:
Vector就是一个线程安全版的Arraylist,这个集合的所有方法都添加了线程同步关键字,在多线程场景下,效率是很低的,我们一般不会使用这个集合,默认初始容量是10,如果存放元素超过10会触发扩容机制,扩容因子是2.0,每触发一次扩容机制,加载为原来的2倍
LinkedList:
这个集合在底层封装了单向链表这种数据结构,所以该集合拥有链表的所有特性,查询效率是不高的,但是随机增删效率很高,而且在LinkedList底层还为我们封装了队列,我们可以去调用入队和出队的方法
Set接口:
HashSet:
在底层为我们聚合了HashMap,在添加元素的时候,实际上调用了HashMap的put方法,元素存放在put方法的key部分
HashSet是一个无序不可重复的集合,存入数据的顺序和取出数据的顺序是不同的
TreeSet:
在底层为我们聚合了TreeMap,在添加元素的时候,实际上调用了TreeMap的put方法,元素存放在put方法的key部分
TreeSet是一个可以按照排序规则自动排序的方法,如果存入元素的引用时我们自定义的,该引用必须要实现比较器接口Comparable<泛型>,重写比较规则,不然会报类型转换异常
Map体系:
HashMap:
可以以key-value的形式去存放数据,key和value都是允许为空的,不是线程安全的集合,但是执行效率很高,在底层封装的是哈希散列
HashTable:
线程安全版的HashMap,执行效率不是很高,key和value都不允许重复
TreeMap:
是可以根据key来完成自动排序的key-value集合,如果存入元素的引用是我们自定义的,该引用必须要实现比较器接口Comparable<泛型>,重写比较规则,不然会报类型转换异常
Properties:
这个集合继承了HashTable,所以该集合是一个线程安全的集合,而且提供了私有的方法SetProper(),getProper(),该集合可以集合流对象高效的去读取配置文件,把数据解析到集合中