第五章 面向对象编程
面向对象:
1、面向对象的概念
I、面向对象就是对生活中的一类事物进行抽象,用Java语言进行描述类的行为和属性
II、面向对象具有很好的通用性和可扩展性
III、面向对象更加注重前期的设计
①就是对类的设计
②设计类的成员:属性和方法
class Person{
String name;
int age;
char gender;
public void eat(Person p) {
System.out.println(p.name+"在吃饭");
}
public void sleep(Person p) {
System.out.println(p.name+"在睡觉");
}
}
2、类是抽象的
类是对生活中的事务进行抽象
3、类和对象
类:是对生活中一类事物的具体化。
对象:是对类的具体化,是实实在在的个体。
对象的属性
1、属性 成员变量 实例变量
属性就是成员变量也是实例变量
2、局部变量和成员变量的区别
①:作用域不同。
②:在内存中的位置不同,局部变量在栈中,成员变量在堆中。
③:局部变量没有初始值,成员变量有初始值。
3、属性的默认值
基本数据类型:
byte short int -->0
long -->0L
float -->0.0F
double -->0.0D
char -->'\u0000'
boolean -->false
引用数据类型(类、接口、数组): --》null
3、属性赋初始值的方式
①:初始值
②:显式赋值,对象名.属性名 this.属性名
③:构造器赋值
参数的值传递:引用数据类型
1、引用数据类型的值传递传递的是地址值
2、当将引用数据类型作为参数,传递给方法
- 方法运行结束后,原属性值会发生改变
3、代码演示
class ReviewTest {
public static void main(String[] args) {
ReviewTest rt = new ReviewTest();
Number1 num = new Number1();
num.a = 10;
num.b = 20;
rt.add(num);
System.out.println("num.a=" + num.a + " num.b=" + num.b);//
}
public void add(Number1 num){
num.a += 1;
num.b += 2;
}
}
4、内存图
参数的值传递:基本数据类型
1、当将基本数据类型作为参数,传递给方法
- 方法运行结束后,原值不会发生改变
2、代码演示
class Demo01{
public static void main(String[] args){
int a = 1;
int b = 2;
add(a,b);
System.out.println("a=" + a + " b=" + b);//a=10 b=20
}
//需求:改变两个数的值
public static void add(int a, int b){//方法运行时,参数一定有值
a += 1;
b += 2;
}
}
3、内存图
内存管理
1、分配:
- 由JVM自动分配内存空间
2、释放:
- 由JVM通过垃圾回收机制释放内存
3、垃圾回收机制(GC)
- 将内存中得垃圾对象自动的从内存中释放
4、垃圾对象
- 不在被任何引用指向的对象
5、代码演示
class GCTest {
public static void main(String[] args) {
Person p = new Person();
p = null;//p指向null,说明p没有被任何引用,此时p就是垃圾对象
}
}
内存管理:
分配:
释放:
垃圾回收机制(GC):将内存中得垃圾对象自动的从内存中释放
垃圾对象:
【面试题】
System.gc():通知垃圾回收机制可以释放内存了,但是垃圾回收机制并不会立即回收,
可以加快垃圾回收机制的运行
面向对象三大特征之一封装性
1、封装的理解
- 该隐藏的隐藏,该暴露的暴露。
2、访问控制修饰符
- public:公共的,可以修饰属性、方法、类 在任何地方都能用。
- private:私有的,可以修饰属性和方法,只能在本类中使用
3、封装的步骤
- 属性私有化(private)
- 提供公共的(public)set/get方法
4、代码演示
class person1 {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
面试题
System.gc():通知垃圾回收机制可以释放内存了,但是垃圾回收机制并不会立即回收,可以加快垃圾回收机制的运行
this关键字
1、核心
- this 使用在本类中,代表当前对象,可用于调用属性、方法、构造器
- 谁让拥有this关键字的方法运行了,谁就是当前对象
2、作用
-
调用属性:this.属性名:区分局部变量和成员变量
-
调用方法:this.方法名
-
调用本类构造器:this(…)
注意:①:避免递归构造器调用
②:this调用构造器必须写在可执行代码的首行
代码演示
构造器
1、构造器:也叫构造方法,是类的成员之一
- 属性
- 方法
- 构造器
2、构造器的格式
访问控制修饰符 类名(参数列表){
//功能语句
}
3、构造器的作用
①创建对象
②用于为对象进行初始化(当对象被创建的同时就具备属性和功能)
4、构造器的注意事项
①构造器的名称必须与类名一致!
②若一个类中没有显示提供任何构造器,系统提供一个默认的无参构造器
public Person(){}
③若一个类中显示的提供了任何构造器,系统默认无参构造器将不再提供
④构造器只能调用一次,并且是在创建对象的时候
⑤构造器之间可以构成重载
5、构造器的重载
- 前提:在同一个类中
①构造器名称相同
②参数列表不同(参数的类型、参数的个数)
6、为属性赋初始值的方式(顺序:①②③)
①默认值(缺省值)
②直接显示赋值
③构造器赋值
第六章:数组的使用
包管理
1、包的作用
①:可用于区分重命名
②:用于控制访问权限
③:可用于划分项目的结构层次,通常将功能相近的类划分到同一个包中
2、关键字
package:用于确定当前类的位置
①:写在当前.java源文件中可执行代码的首行。
②:包的命名规范:所有字母都小写。通常使用所在公司域名的倒置。www.atguigu.com --> com.atguigu.项目名.模块名
③:每个.代表一层目录。
import:用于确定需要引入类的位置
①:写在package和class之间。
②:import语句可以有多条,并排列出。
③:import com.atguigu.aaa.*代表导入aaa包下所有类或接口,
(不包括包)如果是com.atguigu.aaa.bbb.Test,就不能直接用import com.atguigu.aaa.*。
④:若在一个类中需要使用两个相同类名,不同包名的两个类时,如:java.sql.Date; java.util.Date;
导包的方式:选择另外一个使用全限定类名(简称全类名)的方式java.util.Date d2 = new java.util.Date();
⑤:静态导包:import static ,导入一个类中所有的静态成员,可以省略类名。
3、代码演示
package com.atguigu.test1;
import com.atguigu.test2.*;;
public class Demo01 {
int age = 20;
public static void main(String[] args) {
com.atguigu.test2.Demo01 d = new com.atguigu.test2.Demo01();//用全类名唯一确定一个类
d.setAge(10);
System.out.println(d.getAge());
}
}
package com.atguigu.test1;
import static com.atguigu.test2.Demo01.age;
import static com.atguigu.test2.Demo01.name;
public class Demo02 {
public static void main(String[] args) {
// System.out.println(Demo01.age);
//静态导包后可以不用类名调用
System.out.println(age);
}
}
数组
1、数组概念
- 用于批量保存一类数据,时引用数据类型之一。
2、数组声明
- int[] scores;
- String[] names;
- 错误写法:int[5] scores;
3、数组初始化
①:静态初始化:初始化操作和赋值操作同时进行
//正确写法
scores = new int[]{55,66,77,88,99}
//错误写法
scores = new int[5]{55,66,77,88,99}
②:动态初始化:初始化操作和赋值操作分开进行
//正确写法
names = new String[5];
//错误写法
names = new String[3]{22,55,55,88}
③:简单数组:
int arr = {1,2,3,4,5,6};
4、注意:
- 无论是静态初始化还是动态初始化在初始化时必须指明长度(静态初始化JVM自动分配长度)
- 数组中每隔个元素的位置都有唯一的索引值
5、获取数组中的元素
数组名[索引值]
for(int i = 0; i < names.length; i++){
System.out.println(names[i])
}
//通过数组名.length获取数组长度
6、数组的默认值
-
基本数据类型:
- byte short int —>0
- float–>0.0F
- double–>0.0D
- char–>’\u0000’
- boolean–>false
-
引用数据类型
- 类(class)
- 接口(interface)
- 数组([])
7、数组的内存图
byte[] bytes = new byte[5];
bytes[2] = 5;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2k2a7NRU-1592795369190)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\1590996666721.png)]
8、数组的常见异常
①:数组下标越界异常(java.lang.ArrayIndexOutOfBoundsException)
package com.atguigu.shuzu;
public class Demo03 {
public static void main(String[] args) {
int[] arr = new int[5];
arr[5] = 5;
}
}
②:空指针异常(java.lang.NullPointerException)
package com.atguigu.shuzu;
public class Demo03 {
public static void main(String[] args) {
// int[] arr = new int[5];
// arr[5] = 5;
Person[] p = new Person[5];
p[0] = new Person(18,"张三");
for (int i = 0; i < p.length; i++) {
System.out.println(p[i].say());//会出现异常,p[1]p[2]p[3]p[4]都是null没有任何指向
}
}
}
class Person{
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person() {}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public String say() {
return name +"."+age;
}
}
9、数组的遍历
-
普通for循环
for(int i = 0;i<arr.length.i++){ System.out.println(arr[i]) }
-
增强for循环:for(被遍历数组中元素的数据类型 变量名 : 被遍历的数组名)
for(int a:arr){ }
-
改变数组中元素的值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RqE3H1m1-1592795369193)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\1591061738208.png)]
-
区别:增强for循环不适合改变数组中元素的值。
10、数组的特点
- 数组无论静态初始化和动态初始化必须指明长度
- 数组中每个元素都有索引值,下标从0开始,到length-1
课上练习
第一题
- 创建一个char类型的36个元素的数组,前26个元素放置’A’-‘Z‘, 后10个元素放置’0’-'9‘。
使用for循环访问所有元素并打印出来。
package com.atguigu.shuzu;
/**
* 创建一个char类型的36个元素的数组,前26个元素放置'A'-'Z‘, 后10个元素放置'0'-'9‘。
使用for循环访问所有元素并打印出来。
* */
public class Demo02 {
public static void main(String[] args) {
char[] c = new char[36];
for(int i = 0;i < c.length;i++) {
if(i < 26) {
c[i] = (char)('A'+i);
}else {
c[i] = (char)('0'+i-26);
}
}
for (int i = 0; i < c.length; i++) {
System.out.println(c[i]);
}
}
}
第二题
- 创建一个char类型的26个元素的数组,分别 放置’A’-'Z‘。使用for循环访问所有元素并打印出来。
提示:char类型数据运算 ‘A’+1 -> ‘B’,‘0’+1 -> '1‘
package com.atguigu.shuzu;
/**
* 创建一个char类型的26个元素的数组,分别 放置'A'-'Z‘。
使用for循环访问所有元素并打印出来。
提示:char类型数据运算 'A'+1 -> 'B','0'+1 -> '1‘
* */
public class Demo01 {
public static void main(String[] args) {
//1、声明一个数组
char[] character = new char[26];
for(int i = 0;i < character.length;i++) {
character[i] = (char) ('A'+i);
}
for (int i = 0; i < character.length; i++) {
System.out.println(character[i]);
}
}
}
第三题
- 声明一个intArray方法,其参数为整型数组。在main方法中创建20个元素的数组,并将其传递给intArray方法。intArray方法中将数组中存放2开始的20个偶数。然后使用增强型for循环访问所有元素并打印出来。
package com.atguigu.shuzu;
/**
* 声明一个intArray方法,其参数为整型数组。在main方法中创建20个元素的数组,并将其传递给intArray方法。
intArray方法中将数组中存放2开始的20个偶数。然后使用增强型for循环访问所有元素并打印出来。
* */
public class Demo5 {
public static void main(String[] args) {
int[] arr = new int[20];
intArray(arr);
}
public static void intArray(int[] arr) {
for (int i = 0,j = 2; i < arr.length; i++,j+=2) {
arr[i] = j;
}
for (int i : arr) {
System.out.println(i);
}
}
}
第四题
- 从键盘读入学生成绩,找出最高分,并输出学生成绩等级。
成绩>=最高分-10 等级为’A’
成绩>=最高分-20 等级为’B’
成绩>=最高分-30 等级为’C’
其余 等级为’D’
提示:先读入学生人数,根据人数创建int数组,存放学生成绩。
package com.atguigu.shuzu;
import java.util.Scanner;
public class Demo04 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入学生人数");
int a = sc.nextInt();
float[] scores = new float[a];
System.out.println("请输入"+a+"个成绩");
float maxScore = 0;
for (int i = 0; i < scores.length; i++) {
scores[i] = sc.nextFloat();
if(scores[i]>maxScore) {
maxScore = scores[i];
}
}
System.out.println("最高分是:"+maxScore);
char grade = '0';
for (int i = 0; i < scores.length; i++) {
if(scores[i] >= maxScore - 10) {
grade = 'A';
}else if(scores[i] >= maxScore - 20) {
grade = 'B';
}else if(scores[i] >= maxScore - 30) {
grade = 'C';
}else {
grade = 'D';
}
System.out.println("student "+i+" score is "+scores[i]+" garade is "+grade);
}
}
}
第五题
package com.atguigu.day08.practice1;
/**
* 使用二维数组打印一个 10 行杨辉三角.
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
....
【提示】
1. 第一行有 1 个元素 第 n 行有 n 个元素
2. 每一行的第一个元素和最后一个元素都是 1
3. 从第三行开始 对于非第一个元素和最后一个元素的元素.
yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];
* */
public class Demo01 {
public static void main(String[] args) {
int[][] arr = new int[50][];
for (int i = 0; i < arr.length; i++) {
arr[i] = new int[i+1];
for (int j = 0; j < arr[i].length; j++) {
if(j == 0 || j == arr[i].length-1) {
arr[i][j] = 1;
}else {
//yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];
arr[i][j] = arr[i-1][j-1] + arr[i-1][j];
}
}
}
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j]+" ");
}
System.out.println();
}
}
}
二维数组
1、二维数组的声明
数据类型[][] 数组;
2、二维数组初始化
-
静态初始化:初始化操作和赋值操作同时进行
arr = new int[][]{{1,2,3},{4,5,6},{7,8,9}};
-
动态初始化:初始化操作和赋值操作同时进行
//方式一 arr = new int[5][6]; //方式二 arr = new int[5][];{null,{0,0},null,null,null} arr[1] = new int[2]; //错误方式 arr = new int[][5];
3、二维数组的遍历
- 普通for循环遍历
for(int i = 0; i < arr.length; i++){
for(int j = 0; j < arr[i].length; j++){
System.out.println(arr[i][j]);
}
}
- 增强for
for(int[] a: arr){
for(int b : a){
System.out.println(a);
}
}
数组常见算法
1、数组的复制
int[] newArr = new int[arr.length];
for(int i = 0; i < arr.length; i++){
newArr[i] = arr[i];
}
2、数组的反转
for(int i = 0; i < arr.length/2; i++){
int tem = arr[i];
arr[i] = arr[arr.length-1-i];
arr[arr.length-1-i] = tem;
}
3、数组元素的最大值和最小值、平均值、和值
public class Demo {
public static void main(String[] args) {
int[] arr = new int[10];
int max = arr[0],min=arr[0];
int sum = 0;
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random()*100);
if(max<arr[i]) {
max = arr[i];
}
if(min > arr[i]) {
min = arr[i];
}
sum += i;
}
System.out.println("最大值:"+max);
System.out.println("最小值:"+min);
System.out.println("和:"+sum);
System.out.println("平均值:"+sum/arr.length);
}
}
4、数组排序
可变参数
1、格式
//需求:计算任意几个数的和
public int test(int... args){
int sum = 0;
for(int i = 0; i< args.length; i++){
sum += args[i];
}
return sum;
}
//调用
int sum = test(1,2,3,4,5,6);
2、作用
- 当调用者调用时,可以传递0个或多个int类型实参
3、取数据
- 数组怎么用,可变参数怎么用
4、注意:
-
可变参数底层就是数组,所以可变参数和数组参数之间不能构成重载
-
可变参数必须使用在当前参数列表的末尾
public void test(double... d,String... s){ //这种写法是错误的,这样写,jvm识别不出前面需要几个double,后面需要几个String } public void test(double d,String... s){ //正确写法 }
命令行参数
class ArrayTest{
public static void main(String[] args){
for(int i = 0; i < args.length; i++){
System.out.println(args[i]);
}
}
}
第七章:面向对象编程下
继承
1、继承
①:关键字:extends —“扩展” 明确子类是父类的扩展
例如:
class A extends B{}
子类:A 父类(超类、基类、SuperClass):B
②:父类基类超类(SuperClass)
2、继承的好处
- 通过继承,子类可以继承父类中所有的属性,和除了私有的方法之外的方法。
- 在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。
3、继承的注意:
-
不能为了简化代码,获取某功能而继承,若继承,两个类之间必须满足一定的所属关系,可以理解为 :
子类 is 父类 或者 子类继承下来的功能是不是都应该属于B类
-
Java只支持单继承,不支持多继承(一个父类可以有多个子类,但是一个子类只能有一个父类)
-
Java支持多层继承
4、为什么要继承
- 多个类中存在相同的属性和行为时,将这些内容抽象到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。提高了代码的复用性。
- 继承的出现让类和类之间产生了关系,可以创建更为特殊的类型。
- 利于可维护性
5、子类继承父类后,类中各成员的特点
-
属性的特点
①:当子类继承父类后,若子类中出现与父类同名的属性时,创建子类对象调用该属性,实际上调用的是子 类的属性。若要调用父类属性时,需要使用关键字super
②当子类继承父类后,继承父类中所有的属性和方法,包括私有的,只不过因为 private 修饰符的作用,子 类不能直接访问,若需要访问使用公共的 get/set 方法
package com.atguigu.java; /** * * */ public class TestExtends { public static void main(String[] args) { Student s = new Student(); s.setName("glj");//name为父类私有属性,所以只能通过set/get方法访问 System.out.println(s.getName()); System.out.println(s.age);//打印子类的属性值 } } class Person{ private String name; int age = 10; public String getName() { return name; } public void setName(String name) { this.name = name; } } class Student extends Person{ int age = 20; } class Empl extends Person{ int age; String name; }
-
方法
①:当子类继承父类后,若子类出现了和父类方法签名一摸一样的方法时,创建子类对象调用该方法时,实际上运行的是子类的方法。如同将父类的方法覆盖了一样。这是子类重写了父类方法,称为方法的重写。
-
构造器
①当子类继承父类后,子类中的所有构造器的第一行第一句都有一个隐式的super()
- super的作用:调用父类无参构造器
- super()的目的:当子类继承父类后,默认继承了父类所有的属性和方法,所以子类初始化时有必要知道父类是如何初始化的
②若父类没有提供无参构造器,必须在子类的所有构造器中显示调用父类的有参构造器
目的就是保证创建子类对象前先初始化父类
③super()必须当前构造器可执行代码的首行
this()必须当前构造器可执行代码的首行
所以super和this不能同时出现
6、方法的重写(override)
①:前提
- 子类要继承父类。
- 父类方法对子类不适用了,子类可以重写父类的方法。
②:规则
- 方法名必须相同
- 参数列表必须相同
- 子类重写父类方法的访问控制修饰符不能小于父类被重写的方法的访问控制修饰符
- 子类返回值类型必须和父类返回值类型一样或者是父类返回值的子类
class Person{
private String name;
int age = 10;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person sleep() {
Person p = new Person();
return p;
}
}
class Student extends Person{
int age = 20;
//重写父类方法
@Override
public Person sleep() {
Student s = new Student();//返回值是父类的子类
return s;
}
}
7、关键字super
super使用方式和this一摸一样。
super:使用在子类中,代表当前父类对象的引用
8、面试题
方法的重载和方法的重写的区别
1、方法的重载
前提:在同一个类中
方法名必须相同
参数列表必须不同
注意:与返回值类型无关
访问控制修饰符
public 公共的可以修饰属性、方法、类 | 任何地方都可以访问 |
---|---|
protected 受保护的 可用于修饰属性、方法 | 可以在本类中,本包中,子类中 |
default 默认的(缺省的)可用于修饰属性、方法、类 | 只能在本类中、本包中 |
private 私有的,可以修饰属性,方法 | 只能在本类中访问 |
多态
1、定义
一类事物的多种表现形式
2、多态的体现
①:方法的重载与重写
②:对象的多态性
3、对象的多态性
条件:①:继承
②:重写
③:父类引用指向子类对象
4、Java程序的运行
Java程序的运行分两种状态:
在多态的情况下
①:编译时:“看左边”,看的是父类的引用。(父类中不具备子类特有的方法)检查语法,语义,词义
②:运行时:“运行时看右边”,看的是子类对象。分配内存
—————————— 虚拟方法调用(动态绑定)
public class TestExtends{
public static void main(String[] args){
Person p = new Student();//多态,父类引用指向子类对象
p.show();//虚拟方法的调用(动态绑定)实际上调用子类的show方法
//p.show() 执行流程:通过p找到Student对象,通过Student对象去找Student类信息中有没有show方法
//如果有,调用,如果没有去父类的类信息中找,找到然后调用
p.writeWork();//调用子类特有的方法。 编译通不过
//编译时,p是Person类型,Person里面没有writeWork方法,所以编译通不过
Student s = (Student)p;//向下转型
p.writeWork();//可以通过
//打印属性 属性不具备多态性,没有虚拟属性这个说法
System.out.println(p.num);//打印的是父类的属性
//p.num执行流程,通过p找到Student对象,(由于Student继承了Person,
//所以在Student对象的内存中会有一小块内存存储Person类的属性,) 由于p是Person类型,
//所以调用Student对象内存中Person类型中的属性。
}
}
class Person{
int num = 10;
public void show() {
System.out.println("父类的show方法");
}
}
class Student extends Person{
int num = 20;
@Override
public void show() {
System.out.println("子类的show方法");
}
//学生特有的方法
public void writeWork(){
System.out.println("学生做作业");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f7rPvwAR-1592795369196)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\1591354278742.png)]
5、数据类型之间的转换
前提:需要有继承关系
①:向上转型:子类转父类,系统自动完成。
②:向下转型:父类转子类,需要使用强转符“(需要转化的类型)”
可能会引发异常:ClassCastException
6、多态的应用之一 多态数组
public class PolymorphismTest3 {
public static void main(String[] args) {
Person1[] persons = new Person1[5]; //多态数组,不仅可以存 Person1 本类类型的对象及 Person1 子类类型对象
persons[0] = new Person1();
persons[1] = new Man();
persons[2] = new Woman();
persons[3] = new Man();
persons[4] = new Woman();
for(int i = 0; i < persons.length; i++){
/*Person1 p = persons[i]; //多态
p.eat(); //虚拟方法调用(动态绑定)
p.sleep();*/
persons[i].eat();
persons[i].sleep();
}
System.out.println("----------------------------------");
for(Person1 p : persons){//多态
p.eat();//虚拟方法调用
p.sleep();
}
}
}
7、多态的应用之二:多态参数
①:instanceof 运算符
例如:p instanceof Student 判断p引用所指向的对象是否是Student本类类型或其子类类型。是返回true, 否,返回false
②:多态参数的应用
public class PolymorphismTest {
public static void main(String[] args) {
PolymorphismTest p = new PolymorphismTest();
//p.show(new Woman());
/*Student1 stu = new Student1();
pt.show(stu);*/
Man man = new Man();
pt.show(man);
}
//需求:展示一个男人吃饭和睡觉的功能
/*public void show(Man man){
man.eat();
man.sleep();
}
//需求:展示一个女人吃饭和睡觉的功能
public void show(Woman woman){
woman.eat();
woman.sleep();
}*/
public void show(Person1 p){//多态参数:当调用方法时,可以传递 Person 本类类型的对象及Person 子类类型对象
p.eat();//虚拟方法调用(动态绑定)
p.sleep();
//若是男人,则调用男人特有的方法
if(p instanceof Man){
Man man = (Man)p;
man.smoking();
}
}
}
class Student1 extends Person1{
public void eat(){
System.out.println("学生吃大餐");
}
public void sleep(){
System.out.println("学生上课偷偷睡");
}
}
class Man{
public void eat(){
System.out.println("男人吃饭");
}
public void sleep(){
System.out.println("男人睡觉");
}
}
第八章、高级类特性
抽象类
1、为什么要使用抽象类
①:类用于描述现实生活中的一类事物,类中有属性、有方法,方法都有方法体。
某种情况下,父类只能知道子类应该具备一个怎样的方法,但是不能明确知道子类是如何实现该方法。
例如:几何图形(多态练习),所有几何图形都应该具备一个计算面积的方法,但是不同几何图形计算面积 的 方式不同。因此Java提供了解决办法。就是使用抽象类。
②:Java允许父类中只提供一个方法的声明,不提供具体的实现。具体实现交给子类完成,该方法就是抽象方法。
③:有一个或多个抽象方法的类就是抽象类
2、如何使用抽象类
①:关键字abstract 被abstract修饰的类是抽象类。
②:格式:public abstract class 类名 {}
3、注意事项
①:拥有一个或多个抽象方法的类是抽象类。
②:抽象类中可以有非抽象方法。
③:抽象类中可以没有抽象方法。
④:抽象类不能创建实例。
⑤:抽象类中可以声明构造器
目的:子类继承父类后,默认继承父类所有的属性和方法,因此子类有必要知道父类如何初始化的。
public class AbstractTest {
public static void main(String[] args) {
// person p = new Person();抽象类不能创建实例
Chinese c = new Chinese();//本态
c.say();
person p = new Chinese();//多态
p.say();//虚拟方法调用,动态绑定
}
}
abstract class person{
private String name;
private int age;
public person(String name, int age) {
this.name = name;
this.age = age;
}
public person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public abstract void say();
}
class Chinese extends person{
@Override
public void say() {
System.out.println("中国人说汉语");
}
}
class American extends person{
@Override
public void say() {
System.out.println("美国人说英语");
}
}
4、如何使用抽象方法
①:abstract修饰的方法是抽象方法。
②:格式:访问控制修饰符 abstract 返回值类型 方法名 (参数列表); 注意:直接分号结尾,没有方法体。
③:当子类继承父类后,若重写了父类中所有的抽象方法,该类是一个具体的类,可以创建实例。
④:当子类继承父类后,若没有重写父类中“所有”的抽象方法,该类是一个抽象类,不能创建实例。
5、注意:
①:abstract 和static不能同时使用 (因为静态方法属于类,可以用类名调用,而抽象方法没有方法体,不能被 调用)。
②:abstract 和 final 不能同时使用 (final修饰的类不能继承)。
③:abstract 和 private 不能同时使用(private修饰的方法对子类隐藏,子类没办法重写)。
接口
1、为什么要使用接口?
- 接口的意义在于抽象、不拘细节,从而使同类事物在同一高度具有通用及可替代性。
- 系统灵活性增强接口只定义规范,具体的实现交给实现类完成。
- 可以定义多个不相关事物的相同功能。
2、如何使用接口
①:关键字:interface 使用interface定义接口。
②:格式:权限修饰符 interface 接口名{}。
③:接口之间满足has a 的所属关系。
3、注意
①:接口和类是平级的。可以把接口理解为特殊的抽象类
②:接口中只能定义全局静态常量和抽象方法
public static final int NUM = 10;//public static final 都可以省略
public abstract void say();//public abstract都可以省略
③:接口中不能有变量,一般方法,构造器、代码块。
④:接口不能创建实例。
⑤:接口就是用来被实现的。
4、实现接口
①:关键字:implements
例如:public class Test implements AA{}
②:实现接口的类称为实现类,实现类的功能和继承一样,实现类可以继承接口中所有的内容。
③:实现类若实现了接口中的所有抽象方法,则该类为具体类,可以创建实例。
④:实现类若没有实现接口中的所有抽象方法,则该类必须为抽象类,不可以创建实例。
⑤:接口可以多实现,解决了Java中单继承的局限性。
⑥:接口不能继承任何类,接口可以继承接口并且可以多继承接口
⑦:一个类可以继承另一个类并且实现多个接口。 注意:先继承后实现
class A extends B implements C, D{}
5、jdk1.8对于接口的升级
①:接口中的默认方法。
②:接口中的静态方法。
(lambad表达式需要函数式接口,接口中只有一个抽象方法的接口)
6、代码演示
package com.atguigu.java;
/**
* @author glj
* @create 2020-06-15:25
*/
public interface InterfaceTest {
public static void main(String[] args) {
Bird b = new Bird();//本态
b.fly();
Flyer f = new Bird();//多态
f.fly();//虚拟方法调用,动态绑定
}
}
interface Flyer{
int NUM = 100;//public static final省略
public void fly();
}
class Animals{
int NUM = 200;
}
class Bird extends Animals implements Flyer{
int NUM = 10;
@Override
public void fly() {
System.out.println("鸟儿自由自在的飞翔");
System.out.println(NUM);//就近原则。
System.out.println(super.NUM);//父类的NUM
System.out.println(Flyer.NUM);//接口中的NUM,因为接口中的是全局的静态常量,可以用类名调用
}
}
class plane implements Flyer{
@Override
public void fly() {
System.out.println("飞机快速的飞翔");
}
}
内部类
1、分类
- 成员内部类
- 静态内部类
- 非静态内部类
- 局部内部类
- 匿名内部类
- 普通局部内部类
2、成员内部类
①:在类中声明一个类,里面的类称为“内部类”,外面的类称为“外部类”。
I、成员内部类的特点
①:是类的成员之一
到目前为止类的成员有:属性 、 方法 、 构造器 、 代码块、 以及成员内部类。
②:内部类可以使用四种访问控制修饰符。(public protected default private)。
③:可以用static 和final修饰。
④:与类的特征一致
II、内部类的作用
-
可以隐藏类
class Outer{ private class Inner{ //实现了对类的隐藏 } }
III、成员内部类实例的创建
①:静态成员内部类实例对象的创建
外部类名.内部类名 对象名 = new 外部类名.内部类名();
②:非静态成员内部类实例对象的创建
外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
//创建非静态内部类的实例
Father.son son = new Father().new son();
son.setAge(10);
son.setName("哈哈");
System.out.println(son.toString());
//创建静态内部类实例
Father.son1 son1 = new Father.son1();
son1.setAge(11);
son1.setName("呵呵");
System.out.println(son1.toString());
IV、区分成员内部类和外部类中的同名属性
public void show(int age){
System.out.println("局部变量"+age);
System.out.println("内部类对象的属性"+this.age);
System.out.println("外部类的属性"+Father.this.age);
}
V、内部类的访问规则
①: 可以直接访问外部类的成员,包括私有
②: 外部类要想访问内部类成员,必须创建对象
VI、成员内部类的.class字节码文件格式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PNVXW6Q3-1592795369200)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\1591622173173.png)]
3、局部内部类的使用
I、定义
- 定义在一个方法或者一个作用域里面的类
II、格式
class Outer{
public void method(){
class Inner{}
}
}
III、访问时
class Outer {
private int age = 20;
public void method() {
final int age2 = 30;
class Inner {
public void show() {
System.out.println(age);
//从内部类中访问方法内变量age2,需要将变量声明为最终类型。
System.out.println(age2);
}
}
Inner i = new Inner();
i.show();
}
}
IV、匿名内部类
①:定义:
- 一个没有名字的类,是内部类的简化写法
②:格式:
new 类名/接口名 (){
//重写方法
};//分号不能少
接口/父类名称 = new 类名/接口名 (){
//重写方法
};
③:本质
- 其实是继承该类或者实现接口的子类匿名对象
class person2{
public Comparable test(){
//使用内部类做为返回值
return new Comparable(){
java
@Override
public int compareTo(Object o) {
return 0;
}
};
}
}
class person1{
public Comparable test(){
//方式一:创建一个类,实现Comparable接口 然后返回实现类对象
//方式二:由于该实现类只在该方法内部使用,故考虑使用内部类
class ComparableImp implements Comparable{
@Override
public int compareTo(Object o) {
return 0;
}
}
return new ComparableImp();//由于comparable是接口,这里返回接口的实现类对象
}
}
④:局部内部类的.class字节码文件格式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5dkzL95I-1592795369202)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\1591623801615.png)]
数字1、2 是为了区分一个方法中的多个内部类。
枚举类java
1、概念
- 枚举是jdk1.5之后的特性,可以定义有限数量的可穷举的数据集。
- 简而言之,当确定一个类有几个对象时,使用枚举。
2、自定义枚举类
①:私有化构造器
②:类的内部创建对象
package com.atguigu.java;
/**
* @author glj
* @create 2020-06-14:47
*/
public class Weeks {
private String date;
//第二步:类内部创建对象
public static final Weeks MONDAY = new Weeks("星期一");
public static final Weeks TUESDAY = new Weeks("星期一");
public static final Weeks WEDNESDAY = new Weeks("星期一");
public static final Weeks THURSDAY = new Weeks("星期一");
//第一步:构造器私有化
private Weeks(String date){
this.date = date;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
@Override
public String toString() {
return "EnumTest{" +
"date='" + date + '\'' +
'}';
}
}
package com.atguigu.java;
/**
* @author glj
* @create 2020-06-19:36
*/
public class EnumTest {
public static void main(String[] args) {
Weeks monday = Weeks.MONDAY;//静态属性 类名调用
System.out.println(monday);
}
}
3、使用Enmu关键字创建对象
①:关键字enum 用于定义枚举类
package com.atguigu.java;
/**
* @author glj
* @create 2020-06-14:47
*/
public enum Weeks3 {
//类内部对象必须放在当前枚举类可执行代码的首行
MONDAY,//都是public static final 修饰的对象java
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY;
}
②:使用switch-case 判断枚举类型
package com.atguigu.java;
/**
* @author glj
* @create 2020-06-19:36
*/
public class EnumTest {
public static void main(String[] args) {
// Weeks monday = Weeks.MONDAY;
// System.out.println(monday);
Weeks2 monday = Weeks2.MONDAY;
switch (monday){
case MONDAY:
System.out.println("星期一");
break;
case THURSDAY:
System.out.println("星期二");
break;
case WEDNESDAY:
System.out.println("星期三");
break;
case TUESDAY:
System.out.println("星期四");
break;
case FRIDAY:
System.out.println("星期五");
break;
case SATURDAY:
System.out.println("星期六");
break;
case SUNDAY:
System.out.println("星期日");
break;
}
}
}
4、枚举类常用方法
valueof(String name) : 根据枚举类对象的名称,获取对应的枚举类对象。
values():获取当前枚举类中所有的枚举类的对象组成的数组
这两个方法直接用类名调用。
Week[] values = Week.values();
Week week = Week.valueOf("MONDAY");
5、枚举类可以实现接口
- 只实现一次接口中的抽象方法,每个枚举对象都可以调用这个方法。
public enum Weeks3 implements MyInterface{
//类内部对象必须放在当前枚举类可执行代码的首行
MONDAY(),//都是public static final 修java饰的对象
TUESDAY(),
WEDNESDAY(),
THURSDAY(),
FRIDAY(),
SATURDAY(),
SUNDAY();
@Override
public void test() {
//只重写一次
}
}
Weeks3[] values = Weeks3.values();
for(Weeks3 week:values){
week.test();
}
//每个对象都会调用这个方法
- 每个枚举对象都单独实现接口中的抽象方法,这种方式利用的是匿名内部类
package com.atguigu.java;
/**
* @author glj
* @create 2020-06-14:47
*/
public enum Weeks3 implements MyInterface{
//类内部对象必须放在当前枚举类可执行代码的首行
MONDAY {
@Override
public void test() {
System.out.println("星期一");
}
},//都是public static final 修饰的对象
TUESDAY{
@Override
public void test() {
System.out.println("星期二");
}
},
WEDNESDAY{
@Override
public void test() {
System.out.println("星期三");
}
},
THURSDAY {
@Override
public void test() {
System.out.println("星期四");
}
},
FRIDAY {
@Override
public void test() {
System.out.println("星期五");
}
},
SATURDAY {
@Override
public void test() {
System.out.println("星期六");
}
},
SUNDAY {
@Overridejava
public void test() {
System.out.println("星期日");
}
};
}
注解
1、定义
①:注解是 jdk1.5出的特性,是一个代码级别的说明。是一个元数据。
②:在Java中注解以 @注解名 的方式呈现。
2、作用
①:编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
- 使用javadoc test.java 生成文档注释
②:代码分析:通过代码里标识的注解对代码进行分析【使用反射】
③:编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】
3、Java中预定义的注解
①: @Override :用于注解方法,说明该方法必须是重写方法
②: @Deprecated :用于注解属性、方法、类。 说明已经过时
③: @SuppressWarnings :用于抑制编译器警告
- 一般传递参数为all,表示压制所有警告 @SuppressWarnings(“all”)
4、自定义注解
①:使用关键字@interface 定义注解
②:自定义注解自动继承了java.lang.annotation.Annotation
-
public interface MyAnno extends java.lang.annotation.Annotation {}
-
注解本质上就是接口,该接口默认继承Annotation接口
③:接口中的抽象方法在注解中就是属性,此属性有要求:
- 属性以无参数方法的形式来声明,方法名和返回值定义了该属性的名字和类型。我们称之为配置参数
- 属性的返回值只能是八种基本数据类型、String类型、Class类型、enum类型、Annotation类型、以上
所有类型的数组 - 定义了属性可以为其指定初始值,使用default关键字为属性赋默认值,在使用注解时可以不赋值
例如: String value() default “注解”; - 如果定义的注解含有配置参数,那么使用时必须指定参数值,除非它有默认 值。格式是“参数名 = 参数 值”,如果只有一个参数成员,且名称为value, 可以省略“value=”
- 如果只有一个参数成员,建议使用参数名为value
注意:自定义注解必须配上注解的信息处理流程才有意义 - 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略,值与值之间用逗号分隔
package com.atguigu.java;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
/**
* @author glj
* @create 2020-06-20:56
*/
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.CLASS)
public @interface MyAnnotation {
String value() default "hh";
}
5、元注解
①:@Target:用于描述注解作用的位置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BZvqNq6k-1592795369203)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\1591707324641.png)]
ElementType是枚举类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8RY9NyOI-1592795369205)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\1591707354609.png)]
②:@Retention:描述注解被保留的阶段,只能用于修饰一个 Annotation 定义,用于指定该 Annotation 的生命 周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pj21sf8h-1592795369206)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\1591707227610.png)]
RetentionPolicy 是枚举类型
RetentionPolicy取值:
①:RetentionPolicy.SOURCE:在源文件中有效(即源文件保留),编译器直接丢弃这种策略的注释
②:RetentionPolicy.CLASS:在class文件中有效(即class保留),当运行 Java程序时,JVM不会保留注解。 这是默认值
③:RetentionPolicy.RUNTIME:在运行时有效(即运行时保留),当运行Java程序时,JVM会保留注释程。序 可以通过反射获取该注释。
③:@Documented:用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档。默认情况 下,javadoc是不包括注解的。
④:@Inherited:描述注解是否被子类继承。
第九章:使用基础API
包装类
1、八种基本数据类型对应的包装类 如下所示:
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
2、基本数据类型与包装类之间的转换
①:装箱:将基本数据类型转换成对应的包装类
-
通过对应包装类的构造器
-
通过对应包装类的静态方法 valueOf()
②:拆箱:将包装类转换成对应的基本数据类型
- 通过对应包装类对象的 xxxValue()。 xxx:表示对应的基本数据类型
3、自动拆箱和装箱
自动装箱与自动拆箱(jdk1.5后)后出来的
4、Integer类提供了一个小的缓存
-
Integer类提供了一个小的缓存(-128~127)之间,若需要装箱的值在该取值范围内,
则从缓存中取一个 Integer 实例,若超出该取值范围,则重新 new Integer 的实例[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dXFtyzwh-1592795369207)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\1591709572356.png)]
5、基本数据类型、包装类 与 String 之间的转换
① :基本数据类型、包装类 转换成 String
-
String str = a + “”;
-
使用对应包装类的静态方法 toString()
-
使用 String 类中的静态方法 valueOf()
6、String 转换成 基本数据类型、包装类
-
通过对应包装类的构造器
-
通过对应包装类的静态方法 parseXxx(). Xxx代表对应的基本数据类型。注意:没有 parseChar()
-
使用对应包装类的静态方法 valueOf
String概述
1、概述
①:Java.lang.String类是不可变字符序列
public final class String extends Object implements Serializable, Comparable<String>, CharSequence
②:String类对象的创建
//方式一:这种方式,首先会去常量池中找有没有“abc”这个字符串,
//有的话直接返回对应的地址,没有的话在常量池中创建,然后返回对应的地址
String str1 = "abc";
//方式二:这种方式,首先会在堆中创建对象,然后去常量池中找有没有对应的字符串,
//如果有堆中对象直接存储对应的地址值如果没有会在常量池中创建新的字符串,然后队中存储对应的地址值
String str2 = new String("abc");
③:方式一:内存图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r3m2W4Dv-1592795369208)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\1591844781977.png)] ④:方式二内存图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fh0zvSKj-1592795369210)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\1591845222230.png)]
⑤:只要是双引号引起来的都在常量池中。
⑥:任何对字符串的操作都会创建新的字符串,因为字符串底层是字符数组,长度是不可变的。所以字符串是 不可变字符序列。
⑦:两种方式的区别:
String str1 = "abc";//代表一个对象,至少在内存中开辟了一块内存空间。
String str2 = new String("abc");//代表两个对象,至少在内存中开辟了两块内存空间
2、String常用方法
①:获取字符串的方法
String concat(String s);//拼接字符串
public void test1(){
String str1 = "abc";
String str2 = "dEf";
System.out.println(str1.concat(str2));//abcdEf
}
String subString(int beginIndex);//获取从指定位置开始的字符串的子串,包括起始位置
public void test2(){
String str1 = "abcdef";
System.out.println(str1.substring(2));//cdef
}
String substring(int beginIndex, endIndex);//包含头不包含尾
public void test2(){
String str1 = "abcdef";
System.out.println(str1.substring(2,5));//cde
}
String toLowerCase();//转换为小写
String toUpperCase();//转换为大写
public void test3(){
String str1 = "abCDEFgh";
System.out.println(str1.toUpperCase());//ABCDEFGH
System.out.println(str1.toLowerCase());//abcdefgh
}
String trim();//删除首尾空格或制表符
public void test4() {
String str1 = " abCDEFgh ";
String str2 = str1.trim();
System.out.println(str2);//abCDEFgh
}
②:搜索方法
int indexOf(int ch);//获取指定字符在字符串首次出现的位置,若没有指定的字符,返回 -1
int indexOf(String str);
int indexOf(int ch, int fromIndex);//从指定位置开始搜索
int indexOf(String str, int fromIndex);
int lastIndexOf(int ch) : 反向获取指定字符位置
public void test5() {
String str1 = "abCacEaFgh";
System.out.println(str1.indexOf(97));//这儿的数字是字符对应的asci码 结果是0
System.out.println(str1.indexOf('a'));//获取指定字符在字符串中首次出现的位置
System.out.println(str1.indexOf("bC"));//获取指定字符串在字符串中首次出现的位置。
}
③:判断方法
boolean equals(Object obj);//判断内容是否相同
public void test6() {
String str1 = "abCacEaFgh";
System.out.println(str1.equals("abc"));//false
System.out.println(str1.equals("abCacEaFgh"));//true
}
boolean equalsIgnoreCase(String str);//判断是否相等,不考虑大小写
public void test7() {
String str1 = "abCacEaFgh";
System.out.println(str1.equalsIgnoreCase("abcaceafgh"));//true
}
boolean contains(String str);//判断是否包含某字符串
public void test8() {
String str1 = "abCacEaFgh";
System.out.println(str1.contains("abC"));//true
System.out.println(str1.contains("abc"));//false
}
boolean startsWith(String str);//判断是否以指定字符串开始
boolean endsWith(String str);//判断是否以指定字符串结尾
public void test9() {
String str1 = "abCacEaFgh";
System.out.println(str1.startsWith("ab"));//true
System.out.println(str1.endsWith("gh"));//true
System.out.println(str1.startsWith("Ab"));//false
}
boolean isEmpty();//判断字符串是否为空
public void test10() {
String str1 = "abCacEaFgh";
String str2 = "";
System.out.println(str1.isEmpty());//false;
System.out.println(str2.isEmpty());//true
}
④:其他方法
int length();//返回字符串长度
char charAt(int index);//返回索引处的字符
public void test11() {
String str1 = "abCacEaFgh";
char c = str1.charAt(0);
System.out.println(c);//a
}
String replace(char oldCahr, char newCahr);//替换字符串中字符
String[] split(String r);//根据指定符号切割,可以按照正则表达式进行切割
⑤:字符和字符串的相互转化
//将字符数组转换为字符串
//方式一:构造器:
String(char[] ch);//将数组中一部分转换为字符串,从第几个索引位置开始转
String(char[] ch, offset, count);//将数组中一部分转换为字符串,从第几个索引位置开始转,转几个
public void test12() {
char[] arr = {'a','b','c','d','e','f'};
String str = new String(arr);
System.out.println(str);//abcdef
String str1 = new String(arr,0,5);//abcde
System.out.println(str1);
}
//将字符数组转换为字符串
//方式二:静态方法
static String copyValueOf(char[] ch)
static String copyValueOf(char[] ch, offset, count)
static String valueOf(char[])
//将字符串转换字符数组:
char[] toCharArray();
public void test13() {
String str = "abcd";
char[] chars = str.toCharArray();
for (char aChar : chars) {
System.out.println(aChar);
}
}
3、String常用算法题
①:模拟一个trim方法,去除字符串两端的空格。
//方式一:
public static String myTrim(String str) {
String[] split = str.split(" ");
String concat = "";
for (int i = 0; i < split.length; i+=2) {
if((i = i + 2) <split.length)
concat = split[i].concat(split[i + 1]);
}
return concat;
}
//方式二:
public static String myTrim2(String s) {
char[] chars = s.toCharArray();
int start = 0;
int end = chars.length - 1;
while (chars[start] == ' ' && start <= end) {
start++;
}
while (chars[end] == ' ' && start <= end) {
end--;
}
return s.substring(start,end);
}
②:将一个字符串进行反转。将字符串中指定部分进行反转。
//比如将“abcdefg”反转为”abfedcg”
public static String revString(String str,int start, int end){
char[] chars = str.toCharArray();
for (int i = start,j = end; i <= j; i++,j--) {
char tem = chars[i];
chars[i] = chars[j];
chars[j] = tem;
}
return new String(chars);
}
③:获取一个字符串在另一个字符串中出现的次数。
//比如:获取“ ab”在 “abkkcadkabkebfkabkskab”中出现的次数
public static int getCount(String str1,String str2){
int count = 0;
int index = 0;
while((index = str2.indexOf(str1))!= -1){
count++;
str2 = str2.substring(index+str1.length());
}
return count;
}
④:获取两个字符串中最大相同子串。
//比如:str1 = "abcwerthelloyuiodef“;str2 = "cvhellobnm"
//提示:将短的那个串进行长度依次递减的子串与较长的串比较。
⑤:对字符串中字符进行自然顺序排序。
/*提示:
1)字符串变成字符数组。
2)对数组排序,选择,冒泡,Arrays.sort();
3)将排序后的数组变成字符串。
*/
4、StringBuffer 与 StringBuilder
①:StringBuffer 与 StringBuilder是可变的字符序列. 二者具有兼容的 API。
②:StringBuffer : 是线程安全的,因此效率低。
StringBuilder : 是线程不安全的,因此效率高
5、StringBuffer 和 StringBuilder 的常用方法:
①:StringBuffer append(String str) : 添加
②:StringBuffer insert(int offset, String str) : 插入
③:StringBuffer replace(int start, int end, String str):替换
④:int indexOf(String str) :返回子串的位置索引
⑤:int lastIndexOf():反向搜索
⑥:String substring(int start, int end):取子字符串序列
⑦:StringBuffer delete(int start, int end):删除一段字符串
⑧:StringBuffer deleteCharAt(int index):删除指定位置字符
⑨:String toString():转换为String对象
日期Date类
1、Date类概述
-
java.util.Date : 表示特定瞬间,精确到毫秒
-
java.text.DateFormat : 用于格式化时间/日期,但是DateFormat 是一个抽象类
|–java.text.SimpleDateFormat : 是 DateFormat 的子类
-
java.time.* : jdk1.8提供的时间日期 API
2、相关方法
①:构造方法: Date date = new Date();
public void test1(){
Date date = new Date();
System.out.println(date);//Thu Jun 11 18:33:47 CST 2020
long mill = date.getTime();
System.out.println(mill);//1591871627561
Date newDate = new Date(mill);
System.out.println(newDate);//Thu Jun 11 18:33:47 CST 2020
}
②:getTime():获取从1970年1月1日0:0:0到现在所经历的毫秒数
long mill = date.getTime();
System.out.println(mill);//1591871627561java
数学运算类
1、概述
- java.lang.Math : 用于数学运算的类
2、相关方法
①:double ceil(double d) : 返回不小于d的最小整数
②:double floor(double d): 返回不大于d的最大整数
③:int round(float f) : 返回最接近f的int型(四舍五入)
④:long round(double d):返回最接近d的long型
⑤:double abs(double d):取绝对值
⑥:double max(double d1, double d2) : 返回较大值
⑦:int min(int i1, int i2) : 返回较小值
⑧:double random() : 返回一个大于等于0.0并且小于1.0的随机数
大数运算
1、java.math.BigInteger:支持任意精度的整数
2、java.math.BigDecimal : 支持任意精度的浮点数
异常
1、异常概述
- Java 中的异常都是以对象的形式存在的,一旦某句代码发生异常,会在该代码处生成一个异常对象,然后以堆栈式抛出若不对该异常对象进行处理,最终会导致程序的终止运行。
2、异常的结构体系
-
java.lang.Throwable : 所有错误和异常的父类
|--java.lang.Error :错误,一些严重的错误。如:内存溢出、系统错误。我们不在代码中进行处理 |--java.lang.Exception : 异常我们要尽可能的预知并处理的异常。 如:用户输入不匹配、网路连接中断等。
-
|–编译时异常(受检异常 checked):编译时对其进行检查,若不对该异常进行处理,编译不能通过。
|–运行时异常(非受检异常 unchecked):可以保证程序的正常运行,一旦发生异常,会在该代码处生成 一个异常对象 ,然后抛出,若不对该异常对象进行处理,程序终止运行
3、异常的处理
①:java中异常的处理采用抓抛模型
②:“抛”:一旦某句代码发生异常,会在该代码处生成一个异常对象,然后以堆栈式抛出。
③:“抓”:将上述抛出的异常对象进行捕获处理
④:“抓的语法格式”:
try{
//可能发生异常的语句
}catch(异常类型 变量名){
//异常处理的语句
}catch(Exception1 e1){
//异常处理的语句
}catch(Exception2 e2){
//异常处理的语句
}
……
finally{
//一定被执行的语句
}
⑤:”抛的语法格式“:处理异常的方式是将异常抛出,交给调用者做具体的处理
package com.atguigu.java;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
一、异常的处理方式之二:throws
只不过处理异常的方式是将异常抛出,交给调用者做具体的处理
使用在方法的声明处,后面跟异常的类型
*/
public class ExceptionTest3 {
public static void main(String[] args) {
try {
show();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void show() throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("");
fis.read();
fis.close();
}
}
4、catch的使用
①:catch 块可以有多个,一旦某个catch异常类型匹配成功,其他 catch 块将不再执行
②: catch 块可以有多个,若多个 catch 块的异常类型具有子父类关系,则子上父下
③ :try-catch 语句可以嵌套
④ :finall 是可选的,表示一定被执行的语句,即使有 return 语句
5、finally的使用
-
finally是一定被执行的语句,即使出现异常也会执行finally
public static int div(int a, int b){ int n = 0; try{ n = a / b; }catch (ArithmeticException e){ e.printStackTrace(); n += 100; return n;//返回100 }catch (Exception e){ System.out.println("其他异常"); }finally{ n += 200; System.out.println("一定执行的语句"); } } } //最终程序返回的是100,这是因为在栈帧中会有一快区域记录返回值,当程序执行到return 语句时,会先记录返回值 //此时记录的就是100,然后执行finall语句,最后返回100
6、异常的常用方法
①:printStackTrace() : 打印异常的堆栈信息(详细信息)
②:getMessage() : 异常的描述信息
7、常见运行时异常
①:java.lang.ClassCastException : 类型转换异常
②:java.lang.ArrayIndexOutOfBoundsException : 数组下标越界异常
③:java.lang.NullPointerException : 空指针异常
④:java.util.InputMismatchException : 输入不匹配异常
⑤:java.lang.ArithmeticException : 算数异常
8、关键字throw
①:throw是制造异常,可以代替return。
②:throw 不仅可以制造 Java 内置的异常类对象,还可以制造自定义异常类对象
9、自定义异常
①:步骤:
- 声明一个类继承一个异常类。(若该类继承 RuntimeException,则该异常为运行时异常,若继承 Exception 则该异常为编译时异常)
- 编写一个有参构造器,为 getMessage 方法设置值
②:什么时候自定义编译时异常,什么时候自定义运行时异常?
- 如果方法是自己用就定义运行时异常
- 如果方法是给别人调用,那么要定义编译时异常,告诉调用者可能有异常要处理
//例如定义一个登录功能,由于这个登录功能要给别人调用,所以要定义编译时异常,告诉调用者有异常要处理
public boolean login(String userName,String password){
if("admin".equals(userName) && "123456".equals(password)){
return true;
}else{
throw new LoginPasswordErrException("用户名或密码错误");
}
}
public class LoginPasswordErrException extends Exception{
public LoginPasswordErrException(){
}
public LoginPasswordErrException(String massage){
super(massage);
}
}
public class LoginTest {
public static void main(String[] args) {
try {
login("admin", "126");
} catch (UsernamePasswordErrorException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
10、为什么要自定义异常?
①:异常名可以自定义
②:异常的描述信息可以随便些。
第十章:集合
1、集合概述
- 集合就像是一种容器,用于存储、获取和操作对象的容器。
2、集合的特点
①:集合的长度事可变的
②:集合中可以存储任意类型的对象
③:集合中只能存储对象
3、集合框架
java.util.Collection:是集合层次的跟接口
|–java.util.List
|–java.util.ArrayList
|–java.util.LinkedList
|–java.util.Set
|–java.util.HashSet
|–linkedHashSet
|–java.util.TreeSet
4、Collection接口的方法
//添加元素
public void add(Object o);
//获取集合中有效元素的个数
public int size();
//清空集合中所有元素
public void clear();
//判断集合是否为空
public boolean isEmpty();
//删除集合中指定元素 也得用equals方法判断
public void remove(Object obj);
//判断集合中是否包含指定元素
public boolean contains(Object o);//底层通过index(o) 去查找,index通过equals方法查找
public class TestCollection {
@Test
public void Test01(){
Collection coll = new ArrayList();
coll.add(new Person(17,"张三"));
coll.add("abc");
coll.add(123);
coll.add(new String("def"));
boolean b1 = coll.contains(new Person(17, "张三"));
boolean b2 = coll.contains(new String("def"));
System.out.println(b1);//false;//Person 没有重写equals方法,所以是Object类中的equals方法,比较的是两个对象的地址值
System.out.println(b2);//true 这是因为String重写了equals方法,字符串内容一样就是同一个对象
}
}
class Person{
int age;
String name;
public Person() {
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
}
public void addAll(Collection coll);//将集合coll中的所有元素添加到当前集合中
add( coll) 与 addAll(coll)的区别:
// 前者是将集合coll作为一个对象添加到当前集合中
// 后者是将coll中的每一个元素单独添加到当前集合中
public boolean containsAll(Collection coll);//判断coll中的所有元素是否包含在当前集合中
public void removeAll(Collection coll);//删除当前集合与coll中的相同元素
public boolean retainAll(Collection<?> c); //判断两个集合中是否有相同元素,取交集
coll.retainAll(coll2);
System.out.println(coll);//两个集合中的相同元素。
Collection Arrays.asList(T... a);//将数组转化成集合
Collection coll = Arrays.asList(1,2,3,4,5,6);
<T> T[] toArray(T[] a);//将集合转化成数组
5、List集合
I、List集合接口的特点
①:有序的(通过add()添加元素的顺序和获取的顺序是一致的)
②:可以存储重复元素
③:有 索引 (索引从0开始,集合最大的索引是该集合的长度size()-1)
④:可以存储null值
II、List集合遍历方式
①:迭代器:
②:普通for循环
③:增强for循环
④:List特有的迭代器
III、LIst集合特有的方法
void | add(int index, E element) 在指定索引位置插入元素 |
---|---|
boolean | addAll(int index, Collection c) 在指定索引位置添加集合到当前集合中 |
E | get(int index) 获取指定索引位置的元素 |
int | indexOf(Object o) 获取指定元素在集合中的位置 |
E | remove(int index) 删除指定索引位置的元素,并返回删除的元素 |
E | set(int index, E element) 修改指定索引位置的元素 |
List | subList(int fromIndex, int toIndex) 截取子集合 包含头不包含尾 |
IV、List集合接口的实现类
ArrayList
1)特点:
①:底层采用数组结构
②:查询效率快,增删效率慢(和LinkedList 相比)
2)安全性:
集合不是线程安全的,执行效率快(相比List接口下的Vector)
3)ArrayList集合的初始化容量:
①:空参构造器:ArrayList() :JDK6.0(包含)以前:饿汉式,直接初始化一个长度为10的Object类型的数组
JDK7.0(包含)以后:在一开始的时候,先静态初始化一个长度为0且没有任何数
据元素的Object数组;当第一次添加元素的时候,会将这个长度为0且没有任
何数据元素的Object数组重新初始化为一个长度为10的数组。
②:有参构造器:ArrayList(int initialCapacity):自定义集合的初始容量(这个容量不能为负数,否则抛出 IllegalArgumentException异常)
③:ArrayList(Collection<? extends E> c)(了解):初始化一个和参数Collection集合等长的集合,并含有 Collection集合对应的元素
4)ArrayList扩容原理
①:JDK6.0(包含)以前:int newCapacity = (oldCapacity * 3)/2 + 1;
②:int newCapacity = oldCapacity + (oldCapacity >> 1);
LinkedList集合
1)特点:
①:底层采用双向链表结构
②:增删效率高,查询效率低(和ArrayList相比)
2)安全性
集合不是线程安全的,执行效率快(相比List接口下的Vector)
3)LinkedLIst特有方法
void | addFirst(E e) 获取第一个元素 |
---|---|
void | addLast(E e) |
E | getFirst() 获取第一个元素 如果集合为空,抛出NoSuchElementException异常 |
E | getLast() 获取最后一个元素 如果集合为空,抛出NoSuchElementException异常 |
E | removeFirst() 移除第一个元素并获取 如果集合为空,抛出NoSuchElementException异常 |
E | removeLast() 移除最后一个元素并获取如果集合为空,抛出NoSuchElementException异常 |
E | pollFirst() 移除第一个元素并获取 如果集合为空,返回null |
E | pollLast() 移除最后一个元素并获取 如果集合为空,返回null |
E | peekFirst() 获取第一个元素 如果集合为空,返回null |
E | peekLast() 获取最后一个元素 如果集合为空,返回null |
4)自定义堆栈和队列的功能
//栈:先进后出,后进先出
public class MyStack {
private LinkedList l = new LinkedList();
//添加元素
public void add(Object o){
l.addFirst(o);
}
//获取元素
public Object get(){
return l.removeFirst();
}
//判断是否为空
public boolean isNull(){
return l.isEmpty();
}
}
//队列:先进先出
public class MyQueue {
private LinkedList l = new LinkedList();
public void add(Object o){
l.addFirst(o);
}
public Object get(){
return l.removeLast();
}
public boolean isNull(){
return l.isEmpty();
}
}
Vector集合
6、Set集合
I、Set集合特点
①:无序的(通过add()添加元素的顺序和获取的顺序不是一致的)
②:不可以存储重复元素
③:没有索引
④:可以存储null值,只能存一个
II、Set集合遍历方式
①:
②:
③:
III、Set集合的典型实现类
1)HashSet
①:是set接口的典型实现类
②:底层原理 :
ex, Collection c)在指定索引位置添加集合到当前集合中 | | E |
get(int index)获取指定索引位置的元素 | | int |
indexOf(Object o)获取指定元素在集合中的位置 | | E |
remove(int index)删除指定索引位置的元素,并返回删除的元素 | | E |
set(int index, E element)修改指定索引位置的元素 | | List |
subList(int fromIndex, int toIndex)` 截取子集合 包含头不包含尾 |
IV、List集合接口的实现类
ArrayList
1)特点:
①:底层采用数组结构
②:查询效率快,增删效率慢(和LinkedList 相比)
2)安全性:
集合不是线程安全的,执行效率快(相比List接口下的Vector)
3)ArrayList集合的初始化容量:
①:空参构造器:ArrayList() :JDK6.0(包含)以前:饿汉式,直接初始化一个长度为10的Object类型的数组
JDK7.0(包含)以后:在一开始的时候,先静态初始化一个长度为0且没有任何数
据元素的Object数组;当第一次添加元素的时候,会将这个长度为0且没有任
何数据元素的Object数组重新初始化为一个长度为10的数组。
②:有参构造器:ArrayList(int initialCapacity):自定义集合的初始容量(这个容量不能为负数,否则抛出 IllegalArgumentException异常)
③:ArrayList(Collection<? extends E> c)(了解):初始化一个和参数Collection集合等长的集合,并含有 Collection集合对应的元素
4)ArrayList扩容原理
①:JDK6.0(包含)以前:int newCapacity = (oldCapacity * 3)/2 + 1;
②:int newCapacity = oldCapacity + (oldCapacity >> 1);
LinkedList集合
1)特点:
①:底层采用双向链表结构
②:增删效率高,查询效率低(和ArrayList相比)
2)安全性
集合不是线程安全的,执行效率快(相比List接口下的Vector)
3)LinkedLIst特有方法
void | addFirst(E e) 获取第一个元素 |
---|---|
void | addLast(E e) |
E | getFirst() 获取第一个元素 如果集合为空,抛出NoSuchElementException异常 |
E | getLast() 获取最后一个元素 如果集合为空,抛出NoSuchElementException异常 |
E | removeFirst() 移除第一个元素并获取 如果集合为空,抛出NoSuchElementException异常 |
E | removeLast() 移除最后一个元素并获取如果集合为空,抛出NoSuchElementException异常 |
E | pollFirst() 移除第一个元素并获取 如果集合为空,返回null |
E | pollLast() 移除最后一个元素并获取 如果集合为空,返回null |
E | peekFirst() 获取第一个元素 如果集合为空,返回null |
E | peekLast() 获取最后一个元素 如果集合为空,返回null |
4)自定义堆栈和队列的功能
//栈:先进后出,后进先出
public class MyStack {
private LinkedList l = new LinkedList();
//添加元素
public void add(Object o){
l.addFirst(o);
}
//获取元素
public Object get(){
return l.removeFirst();
}
//判断是否为空
public boolean isNull(){
return l.isEmpty();
}
}
//队列:先进先出
public class MyQueue {
private LinkedList l = new LinkedList();
public void add(Object o){
l.addFirst(o);
}
public Object get(){
return l.removeLast();
}
public boolean isNull(){
return l.isEmpty();
}
}
Vector集合
6、Set集合
I、Set集合特点
①:无序的(通过add()添加元素的顺序和获取的顺序不是一致的)
②:不可以存储重复元素
③:没有索引
④:可以存储null值,只能存一个
II、Set集合遍历方式
①:
②:
③:
III、Set集合的典型实现类
1)HashSet
①:是set接口的典型实现类
②:底层原理 :
HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用null元素。HashSet中不允许有重复元素,这是因为HashSet是基于 HashMap实现的,HashSet中的元素都存放在HashMap的key上面,而value中的值都是统一的一个private static final Object PRESENT = new Object();。HashSet跟HashMap一样,都是一个存放链表的数组。