文章目录
- 类变量
- 介绍
- 语法
- 代码示例
- 类方法
- 介绍
- 注意事项
- 代码示例
- 代码块
- 介绍
- 基本语法
- 注意事项
- 设计模式
- 单例设计模式
- 介绍
- 饿汉式
- 懒汉式
- 两者区别
- 模板设计模式
- 介绍
- 代码示例
- final关键字
- 介绍
- 代码示例
- 注意事项
- 抽象类
- 介绍
- 注意事项
- 接口
- 介绍
- 基本语法
- 注意细节
- 接口特性
- 内部类
- 介绍
- 分类
- 局部内部类
- 使用
- 代码示例
- 匿名内部类
- 介绍
- 基本语法
- 代码示例
- 使用细节
- 成员内部类
- 说明
- 代码示例
- 静态内部类
- 使用细节
- 代码示例
- 自定义枚举类
- 介绍
- 使用
- enum枚举类
- 注意事项
- enum常用方法
- 注解
- 介绍
- Annotation介绍
- **Override代码示例**
- Deprecated代码示例
- SuppressWarnings代码示例
- 异常
- 概念
- 异常体系图
- 异常处理
- 介绍
- 方式
- try-catch-finally使用细节
- throws使用细节
- 自定义异常
- 概念
- 步骤
- 代码示例
- 包装类
- 介绍
- 代码示例
- 包装类的常用方法
- integer和string类型相互转换
- 常用方法
- integer类
- 大数处理方案
- String类
- 介绍
- 代码示例
- 创建对象方式
- 常见方法
- 代码示例
- StringBuffer类
- 介绍
- 与string类的区别
- string与stringbuffer的转换
- 常用方法
- StringBuilder类
- 介绍
- 常用方法
- string,stringbuffer,stringbuilder三者的区别和原则
- Math类
- 介绍
- 常用方法
- 代码示例
- Arrays类
- 常用方法
- 代码示例
- System类
- 常用方法
- 代码示例
- 日期类
- 第一代日期类--Date
- 介绍
- 代码示例
- 第二代日期类--Calendar
- 介绍
- 代码示例
- 第三代日期类
- 基本使用
- instant时间戳
- 集合
- 介绍
- 框架体系图
- Collection的常用接口和方法
- Collection的接口的特点
- 常用方法
- 代码示例
- 接口遍历元素方式1--使用Iterator(迭代器)
- 介绍
- 代码示例
- 接口遍历元素方式2--for循环增强
- 介绍
- 特点
- 基本语法
- 代码示例
- List接口和常用方法
- 介绍
- 常用方法
- 三种遍历方式[ArrayList,LinkedList,Vector]
- 代码示例
- 代码示例
- 各节点的底层架构和原理跳过(516)
- 泛型
- 介绍
- 代码示例
- 语法
- 声明
- 实例化
- 代码示例
- 使用细节
- 代码示例
- 代码示例*
- 自定义泛型
- 自定义泛型类
- 语法
- 注意细节
- 代码示例
- 自定义泛型接口
- 基本语法
- 注意细节
- 代码示例
- 自定义泛型方法
- 基本语法
- 使用细节
- 代码示例
- 泛型通配符
- 介绍
- 代码示例
类变量
-
介绍
- 类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。这个从前面的图也可看出来。
-
语法
- 访问修饰符 static 数据类型 变量名;
- static 访问修饰符 数据类型 变量名;
-
代码示例
-
package com.hspedu.static_; public class ChildGame { public static void main(String[] args) { //定义一个变量 count, 统计有多少小孩加入了游戏 int count = 0; Child child1 = new Child("白骨精"); child1.join(); //count++; child1.count++; Child child2 = new Child("狐狸精"); child2.join(); //count++; child2.count++; Child child3 = new Child("老鼠精"); child3.join(); //count++; child3.count++; //=========== //类变量,可以通过类名来访问 System.out.println("共有" + Child.count + " 小孩加入了游戏..."); //下面的代码输出什么? System.out.println("child1.count=" + child1.count);//3 System.out.println("child2.count=" + child2.count);//3 System.out.println("child3.count=" + child3.count);//3 } } class Child { //类 private String name; //定义一个变量 count ,是一个类变量(静态变量) static 静态 //该变量最大的特点就是会被Child 类的所有的对象实例共享 public static int count = 0; public Child(String name) { this.name = name; } public void join() { System.out.println(name + " 加入了游戏.."); } } 输出结果: 白骨精 加入了游戏.. 狐狸精 加入了游戏.. 老鼠精 加入了游戏.. 共有3 小孩加入了游戏... child1.count=3 child2.count=3 child3.count=3
-
类方法
-
介绍
- 当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率。
-
注意事项
- 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区:类方法中无this的参数
普通方法中隐含着this的参数 - 类方法可以通过类名调用,也可以通过对象名调用。
- 普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用。
- 类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以。
- 类方法(静态方法)中只能访问静态变量或静态方法。
- 普通成员方法,既可以访问非静态成员,也可以访问静态成员。
- 总结:静态方法,只能访问静态的成员,非静态的方法,可以访问静态成员和非静态成员
- 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区:类方法中无this的参数
-
代码示例
-
package com.hspedu.static_; public class StaticMethodDetail { public static void main(String[] args) { D.hi();//ok //非静态方法,不能通过类名调用 //D.say();, 错误,需要先创建对象,再调用 new D().say();//可以 } } class D { private int n1 = 100; private static int n2 = 200; public void say() {//非静态方法,普通方法 } public static void hi() {//静态方法,类方法 //类方法中不允许使用和对象有关的关键字, //比如this和super。普通方法(成员方法)可以。 //System.out.println(this.n1); } //类方法(静态方法)中 只能访问 静态变量 或静态方法 //口诀:静态方法只能访问静态成员. public static void hello() { System.out.println(n2); System.out.println(D.n2); //System.out.println(this.n2);不能使用 hi();//OK //say();//错误 } //普通成员方法,既可以访问 非静态成员,也可以访问静态成员 //小结: 非静态方法可以访问 静态成员和非静态成员 public void ok() { //非静态成员 System.out.println(n1); say(); //静态成员 System.out.println(n2); hello(); } } //成功编译
-
代码块
-
介绍
- 代码化块又称为初始化块,属于类中的成员[即是类的一部分],类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
- 代码化块又称为初始化块,属于类中的成员[即是类的一部分],类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。
-
基本语法
- [修饰符]{
代码
};
- [修饰符]{
-
注意事项
-
static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行。
-
类什么时候被加载[重要背!]
- ①创建对象实例时(new)
- ②创建子类对象实例,父类也会被加载
- ③使用类的静态成员时(静态属性,静态方法)案例演示:A类extends B类的静态块
-
普通的代码块,在创建对象实例时,会被隐式的调用。被创建一次,就会调用一次。
如果只是使用类的静态成员时,普通代码块并不会执行。 -
小结:
- static代码块是类加载时,执行,只会执行一次
- 普通代码块是在创建对象时调用的,创建一次,调用一次
- 类加载的3种情况,需要记住.
-
代码示例
-
package com.hspedu.codeblock_; public class CodeBlockDetail01 { public static void main(String[] args) { //类被加载的情况举例 //1. 创建对象实例时(new) // AA aa = new AA(); //2. 创建子类对象实例,父类也会被加载, 而且,父类先被加载,子类后被加载 // AA aa2 = new AA(); //3. 使用类的静态成员时(静态属性,静态方法) // System.out.println(Cat.n1); //static代码块,是在类加载时,执行的,而且只会执行一次. // DD dd = new DD(); // DD dd1 = new DD(); //普通的代码块,在创建对象实例时,会被隐式的调用。 // 被创建一次,就会调用一次。 // 如果只是使用类的静态成员时,普通代码块并不会执行 System.out.println(DD.n1);//8888, 静态模块块一定会执行 } } class DD { public static int n1 = 8888;//静态属性 //静态代码块 static { System.out.println("DD 的静态代码1被执行...");// } //普通代码块, 在new 对象时,被调用,而且是每创建一个对象,就调用一次 //可以这样简单的,理解 普通代码块是构造器的补充 { System.out.println("DD 的普通代码块..."); } } class Animal { //静态代码块 static { System.out.println("Animal 的静态代码1被执行...");// } } class Cat extends Animal { public static int n1 = 999;//静态属性 //静态代码块 static { System.out.println("Cat 的静态代码1被执行...");// } } class BB { //静态代码块 static { System.out.println("BB 的静态代码1被执行...");//1 } } class AA extends BB { //静态代码块 static { System.out.println("AA 的静态代码1被执行...");//2 } } //DD 的静态代码1被执行... //8888
-
-
创建一个对象时,在一个类调用顺序是:(重点,难点):
-
①调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用)
-
②调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)
③调用构造方法 -
代码示例
-
package com.hspedu.codeblock_; public class CodeBlockDetail02 { public static void main(String[] args) { A a = new A();// (1) A 静态代码块01 (2) getN1被调用...(3)A 普通代码块01(4)getN2被调用...(5)A() 构造器被调用 } } class A { { //普通代码块 System.out.println("A 普通代码块01"); } private int n2 = getN2();//普通属性的初始化 static { //静态代码块 System.out.println("A 静态代码块01"); } //静态属性的初始化 private static int n1 = getN1(); public static int getN1() { System.out.println("getN1被调用..."); return 100; } public int getN2() { //普通方法/非静态方法 System.out.println("getN2被调用..."); return 200; } //无参构造器 public A() { System.out.println("A() 构造器被调用"); } } 输出结果: A 静态代码块01 getN1被调用... A 普通代码块01 getN2被调用... A() 构造器被调用
-
-
-
我们看一下创建一个子类对象时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:
- 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
- 子类的静态代码块和静态属性(优先级一样,按定义顺序执行
- 父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
- 父类的构造方法
- 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
- 子类的构造方法
-
静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员。
-
代码示例
-
package com.hspedu.codeblock_; public class CodeBlockDetail04 { public static void main(String[] args) { //老师说明 //(1) 进行类的加载 //1.1 先加载 父类 A02 1.2 再加载 B02 //(2) 创建对象 //2.1 从子类的构造器开始 //new B02();//对象 new C02(); } } class A02 { //父类 private static int n1 = getVal01(); static { System.out.println("A02的一个静态代码块..");//(2) } { System.out.println("A02的第一个普通代码块..");//(5) } public int n3 = getVal02();//普通属性的初始化 public static int getVal01() { System.out.println("getVal01");//(1) return 10; } public int getVal02() { System.out.println("getVal02");//(6) return 10; } public A02() {//构造器 //隐藏 //super() //普通代码和普通属性的初始化...... System.out.println("A02的构造器");//(7) } } class C02 { private int n1 = 100; private static int n2 = 200; private void m1() { } private static void m2() { } static { //静态代码块,只能调用静态成员 //System.out.println(n1);错误 System.out.println(n2);//ok //m1();//错误 m2(); } { //普通代码块,可以使用任意成员 System.out.println(n1); System.out.println(n2);//ok m1(); m2(); } } class B02 extends A02 { // private static int n3 = getVal03(); static { System.out.println("B02的一个静态代码块..");//(4) } public int n5 = getVal04(); { System.out.println("B02的第一个普通代码块..");//(9) } public static int getVal03() { System.out.println("getVal03");//(3) return 10; } public int getVal04() { System.out.println("getVal04");//(8) return 10; } //一定要慢慢的去品.. public B02() {//构造器 //隐藏了 //super() //普通代码块和普通属性的初始化... System.out.println("B02的构造器");//(10) // TODO Auto-generated constructor stub } } //200 //100 //200
-
-
代码示例
-
package com.hspedu.codeblock_; public class CodeBlockExercise02 { } class Sample { Sample(String s) { System.out.println(s); } Sample() { System.out.println("Sample默认构造函数被调用"); } } class Test{ Sample sam1=new Sample("sam1成员初始化");// static Sample sam=new Sample("静态成员sam初始化 ");// static{ System.out.println("static块执行");// if(sam==null)System.out.println("sam is null"); } Test()//构造器 { System.out.println("Test默认构造函数被调用");// } //主方法 public static void main(String str[]) { Test a=new Test();//无参构造器 } } 输出结果: 静态成员sam初始化 static块执行 sam1成员初始化 Test默认构造函数被调用
-
-
设计模式
-
单例设计模式
-
介绍
- 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(需要用到代码块的知识)
-
饿汉式
-
代码示例
-
package com.hspedu.single_; public class SingleTon01 { public static void main(String[] args) { // GirlFriend xh = new GirlFriend("小红"); // GirlFriend xb = new GirlFriend("小白"); //通过方法可以获取对象 GirlFriend instance = GirlFriend.getInstance(); System.out.println(instance); GirlFriend instance2 = GirlFriend.getInstance(); System.out.println(instance2); System.out.println(instance == instance2);//T //System.out.println(GirlFriend.n1); //... } } //有一个类, GirlFriend //只能有一个女朋友 class GirlFriend { private String name; //public static int n1 = 100; //为了能够在静态方法中,返回 gf对象,需要将其修饰为static //對象,通常是重量級的對象, 餓漢式可能造成創建了對象,但是沒有使用. private static GirlFriend gf = new GirlFriend("小红红"); //如何保障我们只能创建一个 GirlFriend 对象 //步骤[单例模式-饿汉式] //1. 将构造器私有化 //2. 在类的内部直接创建对象(该对象是static) //3. 提供一个公共的static方法,返回 gf对象 private GirlFriend(String name) { System.out.println("構造器被調用."); this.name = name; } public static GirlFriend getInstance() { return gf; } @Override public String toString() { return "GirlFriend{" + "name='" + name + '\'' + '}'; } } 输出结果: 構造器被調用. GirlFriend{name='小红红'} GirlFriend{name='小红红'} true
-
-
-
懒汉式
-
代码示例
-
package com.hspedu.single_; /** * 演示懶漢式的單例模式 */ public class SingleTon02 { public static void main(String[] args) { //new Cat("大黃"); //System.out.println(Cat.n1); Cat instance = Cat.getInstance(); System.out.println(instance); //再次調用getInstance Cat instance2 = Cat.getInstance(); System.out.println(instance2); System.out.println(instance == instance2);//T } } //希望在程序運行過程中,只能創建一個Cat對象 //使用單例模式 class Cat { private String name; public static int n1 = 999; private static Cat cat ; //默認是null //步驟 //1.仍然構造器私有化 //2.定義一個static靜態屬性對象 //3.提供一個public的static方法,可以返回一個Cat對象 //4.懶漢式,只有當用戶使用getInstance時,才返回cat對象, 後面再次調用時,會返回上次創建的cat對象 // 從而保證了單例 private Cat(String name) { System.out.println("構造器調用..."); this.name = name; } public static Cat getInstance() { if(cat == null) {//如果還沒有創建cat對象 cat = new Cat("小可愛"); } return cat; } @Override public String toString() { return "Cat{" + "name='" + name + '\'' + '}'; } } 输出结果: 構造器調用... Cat{name='小可愛'} Cat{name='小可愛'} true
-
-
-
两者区别
- 二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建。
- 饿汉式不存在线程安全问题,懒汉式存在线程安全问题。(后面学习线程后,会完善一把)
- 饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题。
- 在我们javaSE标准类中,java.lang.Runtime就是经典的单例模式。
-
-
模板设计模式
-
介绍
-
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。(需要用到抽象类的知识)
-
能解决的问题
- 当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式.
- 当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
-
-
代码示例
-
需求:
- 有多个类,完成不同的任务job
- 要求统计得到各自完成任务的时间
- 请编程实现
-
代码
-
AA.java: package com.hspedu.abstract_; public class AA extends Template { //计算任务 //1+....+ 800000 @Override public void job() { //实现Template的抽象方法job long num = 0; for (long i = 1; i <= 800000; i++) { num += i; } } // public void job2() { // //得到开始的时间 // long start = System.currentTimeMillis(); // long num = 0; // for (long i = 1; i <= 200000; i++) { // num += i; // } // //得的结束的时间 // long end = System.currentTimeMillis(); // System.out.println("AA 执行时间 " + (end - start)); // } } ================================================================================= BB.java: package com.hspedu.abstract_; public class BB extends Template{ public void job() {//这里也去,重写了Template的job方法 long num = 0; for (long i = 1; i <= 80000; i++) { num *= i; } } } ================================================================================= Template.java//模版文件 package com.hspedu.abstract_; abstract public class Template { //抽象类-模板设计模式 public abstract void job();//抽象方法 public void calculateTime() {//实现方法,调用job方法 //得到开始的时间 long start = System.currentTimeMillis(); job(); //动态绑定机制 //得的结束的时间 long end = System.currentTimeMillis(); System.out.println("任务执行时间 " + (end - start)); } } ================================================================================= TestTemplate .java//运行文件 package com.hspedu.abstract_; public class TestTemplate { public static void main(String[] args) { AA aa = new AA(); aa.calculateTime(); //这里还是需要有良好的OOP基础,对多态 BB bb = new BB(); bb.calculateTime();//这里还是需要有良好的OOP基础,对多态 } }
-
-
-
final关键字
-
介绍
-
final 可以修饰类、属性、方法和局部变量.
在某些情况下,程序员可能有以下需求,就会使用到final:- 当不希望类被继承时,可以用final修饰.
- 当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。
- 当不希望类的的某个属性的值被修改,可以用final修饰.
- 当不希望某个局部变量被修改,可以使用final修饰
-
-
代码示例
-
package com.hspedu.final_; public class Final01 { public static void main(String[] args) { E e = new E(); //e.TAX_RATE = 0.09; } } //如果我们要求A类不能被其他类继承 //可以使用final修饰 A类 final class A { } //class B extends A {} class C { //如果我们要求hi不能被子类重写 //可以使用final修饰 hi方法 public final void hi() {} } class D extends C { // @Override // public void hi() { // System.out.println("重写了C类的hi方法.."); // } } //当不希望类的的某个属性的值被修改,可以用final修饰 class E { public final double TAX_RATE = 0.08; } //当不希望某个局部变量被修改,可以使用final修饰 class F { public void cry() { //这时,NUM 也称为 局部常量 final double NUM = 0.01; //NUM = 0.9; System.out.println("NUM=" + NUM); } } //编译成功
-
-
注意事项
-
final修饰的属性又叫常量,一般用XX XX XX来命名
-
final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一【选择一个位置赋初值即可】:
- 定义时:如 public final double TAX RATE=0.08;
- 在构造器中
- 在代码块中。
-
3)如果final修饰的属性是静态的,则初始化的位置只能是
- 定义时
- 在静态代码块不能在构造器中赋值。
-
final类不能继承,但是可以实例化对象。[A2类]
-
如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。[A3类]
-
代码示例
-
package com.hspedu.final_; public class FinalDetail01 { public static void main(String[] args) { CC cc = new CC(); new EE().cal(); } } class AA { /* 1. 定义时:如 public final double TAX_RATE=0.08; 2. 在构造器中 3. 在代码块中 */ public final double TAX_RATE = 0.08;//1.定义时赋值 public final double TAX_RATE2 ; public final double TAX_RATE3 ; public AA() {//构造器中赋值 TAX_RATE2 = 1.1; } {//在代码块赋值 TAX_RATE3 = 8.8; } } class BB { /* 如果final修饰的属性是静态的,则初始化的位置只能是 1 定义时 2 在静态代码块 不能在构造器中赋值。 */ public static final double TAX_RATE = 99.9; public static final double TAX_RATE2 ; static { TAX_RATE2 = 3.3; } } //final类不能继承,但是可以实例化对象 final class CC { } //如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承 //即,仍然遵守继承的机制. class DD { public final void cal() { System.out.println("cal()方法"); } } class EE extends DD { } //cal()方法
-
-
一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。
-
final不能修饰构造方法(即构造器)
-
final 和 static 往往搭配使用,效率更高,不会导致类加载.底层编译器做了优化
处理。 -
包装类(Integer,Double,Float,Boolean等都是final),String也是final类。
-
注意事项
-
package com.hspedu.final_; public class FinalDetail02 { public static void main(String[] args) { System.out.println(BBB.num); System.out.println("=================="); System.out.println(BBB.num1); //包装类,String 是final类,不能被继承 } } //final 和 static 往往搭配使用,效率更高,不会导致类加载.底层编译器做了优化处理 class BBB { public final static int num = 10000; public static int num1 = 10000; static { System.out.println("BBB 静态代码块被执行"); } } final class AAA{ //一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法 //public final void cry() {} } 输出结果: 10000 ================== BBB 静态代码块被执行 10000
-
-
抽象类
-
介绍
- 当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类
- 用abstract 关键字来修饰一个类时,这个类就叫抽象类
访问修饰符 abstract 类名{
} - 用abstract关键字来修饰一个方法时,这个方法就是抽象方法
访问修饰符abstract 返回类型方法名(参数列表);//没有方法体 - 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类()
- 抽象类,是考官比较爱问的知识点,在框架和设计模式使用较多
-
注意事项
-
抽象类不能被实例化
-
抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
-
一旦类包含了abstract方法,则这个类必须声明为abstract
-
abstract 只能修饰类和方法,不能修饰属性和其它的。
-
代码示例
-
package com.hspedu.abstract_; public class AbstractDetail01 { public static void main(String[] args) { //抽象类,不能被实例化 //new A(); } } //抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法 //,还可以有实现的方法。 abstract class A { public void hi() { System.out.println("hi"); } } //一旦类包含了abstract方法,则这个类必须声明为abstract abstract class B { public abstract void hi(); } //abstract 只能修饰类和方法,不能修饰属性和其它的 class C { // public abstract int n1 = 1; } //成功运行
-
-
5)抽象类可以有任意成员【抽象类本质还是类】,比如:非抽象方法、构造器、静态属性等等
6)抽象方法不能有主体,即不能实现.如abstract void aaa(){}
7)如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类。 -
代码示例
-
package com.hspedu.abstract_; public class AbstractDetail01 { public static void main(String[] args) { //抽象类,不能被实例化 //new A(); } } //抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法 //,还可以有实现的方法。 abstract class A { public void hi() { System.out.println("hi"); } } //一旦类包含了abstract方法,则这个类必须声明为abstract abstract class B { public abstract void hi(); } //abstract 只能修饰类和方法,不能修饰属性和其它的 class C { // public abstract int n1 = 1; } //成功编译
-
-
接口
-
介绍
- 接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。
- 接口是更加抽象的抽象的类,抽象类里的方法可以有方法体,接口里的所有方法都没有方法体【jdk7.0】。接口体现了程序设计的多态和高内聚低偶合的设计思想。
-
基本语法
- interface 接口名{
属性;
抽象方法;
} - class 类名 implements 接口{
自己属性;
自己方法;
必须实现的接口方法;
}
- interface 接口名{
-
注意细节
-
接口不能被实例化
-
接口中所有的方法是public方法,接口中抽象方法,可以不用abstract修饰
-
一个普通类实现接口,就必须将该接口的所有方法都实现。
-
抽象类实现接口,可以不用实现接口的方法。
-
代码示例
-
package com.hspedu.interface_; public class InterfaceDetail01 { public static void main(String[] args) { //new IA(); } } //1.接口不能被实例化 //2.接口中所有的方法是 public方法, 接口中抽象方法,可以不用abstract 修饰 //3.一个普通类实现接口,就必须将该接口的所有方法都实现,可以使用alt+enter来解决 //4.抽象类去实现接口时,可以不实现接口的抽象方法 interface IA { void say();//修饰符 public protected 默认 private void hi(); } class Cat implements IA{ @Override public void say() { } @Override public void hi() { } } abstract class Tiger implements IA { } //编译成功
-
-
一个类同时可以实现多个接口[举例]
-
接口中的属性,只能是final的,而且是 public static final 修饰符。比如:
int a=1;实际上是 public static final int a=1;(必须初始化) -
接口中属性的访问形式:接口名.属性名
-
接口不能继承其它的类,但是可以继承多个别的接口[举例]
interface A extends B,C(} -
接口的修饰符只能是public 和默认,这点和类的修饰符是一样的。
-
代码示例
-
package com.hspedu.interface_; public class InterfaceDetail02 { public static void main(String[] args) { //老韩证明 接口中的属性,是 public static final System.out.println(IB.n1);//说明n1 就是static //IB.n1 = 30; 说明n1 是 final } } interface IB { //接口中的属性,只能是final的,而且是 public static final 修饰符 int n1 = 10; //等价 public static final int n1 = 10; void hi(); } interface IC { void say(); } //接口不能继承其它的类,但是可以继承多个别的接口 interface ID extends IB,IC { } //接口的修饰符 只能是 public 和默认,这点和类的修饰符是一样的 interface IE{} //一个类同时可以实现多个接口 class Pig implements IB,IC { @Override public void hi() { } @Override public void say() { } } //10
-
-
-
接口特性
-
在前面的Usb接口案例,UsbInterface usb,既可以接收手机对象,又可以接收相机对象,就体现了接口多态(接口引用可以指向实现了接口的类的对象)
-
package com.hspedu.interface_; public class InterfacePolyParameter { public static void main(String[] args) { //接口的多态体现 //接口类型的变量 if01 可以指向 实现了IF接口类的对象实例 IF if01 = new Monster(); if01 = new Car(); //继承体现的多态 //父类类型的变量 a 可以指向 继承AAA的子类的对象实例 AAA a = new BBB(); a = new CCC(); } } interface IF {} class Monster implements IF{} class Car implements IF{} class AAA { } class BBB extends AAA {} class CCC extends AAA {} //成功编译
-
-
给Usb数组中,存放Phone和相机对象,Phone类还有一个特有的方法call(),请遍历Usb数组,如果是Phone对象,除了调用Usb接口定义的方法外,还需要调用Phone 特有方法 call.
-
package com.hspedu.interface_; public class InterfacePolyArr { public static void main(String[] args) { //多态数组 -> 接口类型数组 Usb[] usbs = new Usb[2]; usbs[0] = new Phone_(); usbs[1] = new Camera_(); /* 给Usb数组中,存放 Phone 和 相机对象,Phone类还有一个特有的方法call(), 请遍历Usb数组,如果是Phone对象,除了调用Usb 接口定义的方法外, 还需要调用Phone 特有方法 call */ for(int i = 0; i < usbs.length; i++) { usbs[i].work();//动态绑定.. //和前面一样,我们仍然需要进行类型的向下转型 if(usbs[i] instanceof Phone_) {//判断他的运行类型是 Phone_ ((Phone_) usbs[i]).call(); } } } } interface Usb{ void work(); } class Phone_ implements Usb { public void call() { System.out.println("手机可以打电话..."); } @Override public void work() { System.out.println("手机工作中..."); } } class Camera_ implements Usb { @Override public void work() { System.out.println("相机工作中..."); } } //手机工作中... //手机可以打电话... //相机工作中...
-
-
接口多态传递
-
package com.hspedu.interface_; /** * 演示多态传递现象 */ public class InterfacePolyPass { public static void main(String[] args) { //接口类型的变量可以指向,实现了该接口的类的对象实例 IG ig = new Teacher(); //如果IG 继承了 IH 接口,而Teacher 类实现了 IG接口 //那么,实际上就相当于 Teacher 类也实现了 IH接口. //这就是所谓的 接口多态传递现象. IH ih = new Teacher(); } } interface IH { void hi(); } interface IG extends IH{ } class Teacher implements IG { @Override public void hi() { } }
-
-
内部类
-
介绍
- 个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员【思考:类的五大成员是哪些?[属性、方法、构造器、代码块、内部类]】,内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系,注意:内部类是学习的难点,同时也是重点,后面看底层源码时,有大量的内部类.
-
分类
-
定义类在局部位置(方法中/代码块)
- 局部内部类
- 匿名内部类
-
定义类在成员位置
- 成员内部类
- 静态内部类
-
-
局部内部类
-
使用
-
说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
-
可以直接访问外部类的所有成员,包含私有的
-
不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final
-
作用域:仅仅在定义它的方法或代码块中。
-
局部内部类——访问——>外部类的成员[访问方式:直接访问]5.外部类—访问——>局部内部类的成员
访问方式:创建对象,再访问(注意:必须在作用域内) -
记住:
- 局部内部类定义在方法中/代码块
- 作用域在方法体或者代码块中
- 本质仍然是一个类
-
外部其他类———不能访问———>局部内部类(因为局部内部类地位是一个局部变量)
-
如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问【演示】
-
-
代码示例
-
package com.hspedu.innerclass; /** * 演示局部内部类的使用 */ public class LocalInnerClass {// public static void main(String[] args) { //演示一遍 Outer02 outer02 = new Outer02(); outer02.m1(); System.out.println("outer02的hashcode=" + outer02); } } class Outer02 {//外部类 private int n1 = 100; private void m2() { System.out.println("Outer02 m2()"); }//私有方法 public void m1() {//方法 //1.局部内部类是定义在外部类的局部位置,通常在方法 //3.不能添加访问修饰符,但是可以使用final 修饰 //4.作用域 : 仅仅在定义它的方法或代码块中 final class Inner02 {//局部内部类(本质仍然是一个类) //2.可以直接访问外部类的所有成员,包含私有的 private int n1 = 800; public void f1() { //5. 局部内部类可以直接访问外部类的成员,比如下面 外部类n1 和 m2() //7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员, // 使用 外部类名.this.成员)去访问 // 老韩解读 Outer02.this 本质就是外部类的对象, 即哪个对象调用了m1, Outer02.this就是哪个对象 System.out.println("n1=" + n1 + " 外部类的n1=" + Outer02.this.n1); System.out.println("Outer02.this hashcode=" + Outer02.this); m2(); } } //6. 外部类在方法中,可以创建Inner02对象,然后调用方法即可 Inner02 inner02 = new Inner02(); inner02.f1(); } } 输出结果: n1=800 外部类的n1=100 Outer02.this hashcode=com.hspedu.innerclass.Outer02@1b6d3586 Outer02 m2() outer02的hashcode=com.hspedu.innerclass.Outer02@1b6d3586
-
-
-
匿名内部类
-
介绍
- 本质是类
- 内部类
- 该类没有名字
-
基本语法
- new 类或接口(参数列表){
类体
};
- new 类或接口(参数列表){
-
代码示例
-
示例一
-
package com.hspedu.innerclass; /** * 演示匿名内部类的使用 */ public class AnonymousInnerClass { public static void main(String[] args) { Outer04 outer04 = new Outer04(); outer04.method(); } } class Outer04 { //外部类 private int n1 = 10;//属性 public void method() {//方法 //基于接口的匿名内部类 //老韩解读 //1.需求: 想使用IA接口,并创建对象 //2.传统方式,是写一个类,实现该接口,并创建对象 //3.老韩需求是 Tiger/Dog 类只是使用一次,后面再不使用 //4. 可以使用匿名内部类来简化开发 //5. tiger的编译类型 ? IA //6. tiger的运行类型 ? 就是匿名内部类 Outer04$1 /* 我们看底层 会分配 类名 Outer04$1 class Outer04$1 implements IA { @Override public void cry() { System.out.println("老虎叫唤..."); } } */ //7. jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并且把地址 // 返回给 tiger //8. 匿名内部类使用一次,就不能再使用 IA tiger = new IA() { @Override public void cry() { System.out.println("老虎叫唤..."); } }; System.out.println("tiger的运行类型=" + tiger.getClass()); tiger.cry(); tiger.cry(); tiger.cry(); // IA tiger = new Tiger(); // tiger.cry(); //演示基于类的匿名内部类 //分析 //1. father编译类型 Father //2. father运行类型 Outer04$2 //3. 底层会创建匿名内部类 /* class Outer04$2 extends Father{ @Override public void test() { System.out.println("匿名内部类重写了test方法"); } } */ //4. 同时也直接返回了 匿名内部类 Outer04$2的对象 //5. 注意("jack") 参数列表会传递给 构造器 Father father = new Father("jack"){ @Override public void test() { System.out.println("匿名内部类重写了test方法"); } }; System.out.println("father对象的运行类型=" + father.getClass());//Outer04$2 father.test(); //基于抽象类的匿名内部类 Animal animal = new Animal(){ @Override void eat() { System.out.println("小狗吃骨头..."); } }; animal.eat(); } } interface IA {//接口 public void cry(); } //class Tiger implements IA { // // @Override // public void cry() { // System.out.println("老虎叫唤..."); // } //} //class Dog implements IA{ // @Override // public void cry() { // System.out.println("小狗汪汪..."); // } //} class Father {//类 public Father(String name) {//构造器 System.out.println("接收到name=" + name); } public void test() {//方法 } } abstract class Animal { //抽象类 abstract void eat(); } 输出结果: tiger的运行类型=class com.hspedu.innerclass.Outer04$1 老虎叫唤... 老虎叫唤... 老虎叫唤... 接收到name=jack father对象的运行类型=class com.hspedu.innerclass.Outer04$2 匿名内部类重写了test方法 小狗吃骨头...
-
-
示例二
-
package com.hspedu.innerclass; import com.hspedu.abstract_.AA; public class InnerClassExercise01 { public static void main(String[] args) { //当做实参直接传递,简洁高效 f1(new IL() { @Override public void show() { System.out.println("这是一副名画~~..."); } }); //传统方法 System.out.println("====================="); f1(new Picture()); } //静态方法,形参是接口类型 public static void f1(IL il) { il.show(); System.out.println("12313"); } } //接口 interface IL { void show(); } //类->实现IL => 编程领域 (硬编码) class Picture implements IL { @Override public void show() { System.out.println("这是一副名画XX..."); } } 输出结果: 这是一副名画~~... 12313 ===================== 这是一副名画XX... 12313
-
-
-
-
使用细节
-
匿名内部类的语法比较奇特,请大家注意,因为匿名内部类既是一个类的定义同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法。
-
可以直接访问外部类的所有成员,包含私有的
-
不能添加访问修饰符,因为它的地位就是一个局部变量。
-
作用域:仅仅在定义它的方法或代码块中。[过]
-
匿名内部类----访问----->外部类成员[访问方式:直接访问]
-
外部其他类——不能访问——>匿名内部类(因为匿名内部类地位是一个局部变量)
-
如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
-
代码实例
-
package com.hspedu.innerclass; public class AnonymousInnerClassDetail { public static void main(String[] args) { Outer05 outer05 = new Outer05(); outer05.f1(); //外部其他类---不能访问----->匿名内部类 System.out.println("main outer05 hashcode=" + outer05); } } class Outer05 { private int n1 = 99; public void f1() { //创建一个基于类的匿名内部类 //不能添加访问修饰符,因为它的地位就是一个局部变量 //作用域 : 仅仅在定义它的方法或代码块中 Person p = new Person(){ private int n1 = 88; @Override public void hi() { //可以直接访问外部类的所有成员,包含私有的 //如果外部类和匿名内部类的成员重名时,匿名内部类访问的话, //默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问 System.out.println("匿名内部类重写了 hi方法 n1=" + n1 + " 外部内的n1=" + Outer05.this.n1 ); //Outer05.this 就是调用 f1的 对象 System.out.println("Outer05.this hashcode=" + Outer05.this); } }; p.hi();//动态绑定, 运行类型是 Outer05$1 //也可以直接调用, 匿名内部类本身也是返回对象 // class 匿名内部类 extends Person {} // new Person(){ // @Override // public void hi() { // System.out.println("匿名内部类重写了 hi方法,哈哈..."); // } // @Override // public void ok(String str) { // super.ok(str); // } // }.ok("jack"); } } class Person {//类 public void hi() { System.out.println("Person hi()"); } public void ok(String str) { System.out.println("Person ok() " + str); } } //抽象类/接口... 输出结果: 匿名内部类重写了 hi方法 n1=88 外部内的n1=99 Outer05.this hashcode=com.hspedu.innerclass.Outer05@1b6d3586 main outer05 hashcode=com.hspedu.innerclass.Outer05@1b6d3586
-
-
-
成员内部类
-
说明
- 可以直接访问外部类所有成员,包含私有的
- 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
- 作用域MemberlnnerClass01.java 和外部类的其他成员一样,为整个类体比如前面案例,在外部类的成员方法中创建成员内部类对象,再调用方法.
- 成员内部类——访问——>外部类成员(比如:属性)[访问方式:直接访问](说明)
- 外部类——访问———>成员内部类(说明)访问方式:创建对象,再访问
- 外部其他类——访问——>成员内部类
- 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
-
代码示例
-
package com.hspedu.innerclass; public class MemberInnerClass01 { public static void main(String[] args) { Outer08 outer08 = new Outer08(); outer08.t1(); //外部其他类,使用成员内部类的三种方式 //老韩解读 // 第一种方式 // outer08.new Inner08(); 相当于把 new Inner08()当做是outer08成员 // 这就是一个语法,不要特别的纠结. Outer08.Inner08 inner08 = outer08.new Inner08(); inner08.say(); // 第二方式 在外部类中,编写一个方法,可以返回 Inner08对象 Outer08.Inner08 inner08Instance = outer08.getInner08Instance(); inner08Instance.say(); } } class Outer08 { //外部类 private int n1 = 10; public String name = "张三"; private void hi() { System.out.println("hi()方法..."); } //1.注意: 成员内部类,是定义在外部内的成员位置上 //2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员 public class Inner08 {//成员内部类 private double sal = 99.8; private int n1 = 66; public void say() { //可以直接访问外部类的所有成员,包含私有的 //如果成员内部类的成员和外部类的成员重名,会遵守就近原则. //,可以通过 外部类名.this.属性 来访问外部类的成员 System.out.println("n1 = " + n1 + " name = " + name + " 外部类的n1=" + Outer08.this.n1); hi(); } } //方法,返回一个Inner08实例 public Inner08 getInner08Instance(){ return new Inner08(); } //写方法 public void t1() { //使用成员内部类 //创建成员内部类的对象,然后使用相关的方法 Inner08 inner08 = new Inner08(); inner08.say(); System.out.println(inner08.sal); } } 输出结果: n1 = 66 name = 张三 外部类的n1=10 hi()方法... 99.8 n1 = 66 name = 张三 外部类的n1=10 hi()方法... n1 = 66 name = 张三 外部类的n1=10 hi()方法...
-
-
-
静态内部类
-
使用细节
- 说明:静态内部类是定义在外部类的成员位置,并且有static修饰
- 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
- 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是 一个成员。
- 作用域:同其他的成员,为整个类体
- 静态内部类——访问——>外部类(比如:静态属性)[访问方式:直接访问所有静态成员]
- 外部类——访问——>静态内部类访问方式:创建对象,再访问
- 外部其他类——访问——>静态内部类
- 如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问
-
代码示例
-
package com.hspedu.innerclass; public class StaticInnerClass01 { public static void main(String[] args) { Outer10 outer10 = new Outer10(); outer10.m1(); //外部其他类 使用静态内部类 //方式1 //因为静态内部类,是可以通过类名直接访问(前提是满足访问权限) Outer10.Inner10 inner10 = new Outer10.Inner10(); inner10.say(); //方式2 //编写一个方法,可以返回静态内部类的对象实例. Outer10.Inner10 inner101 = outer10.getInner10(); System.out.println("============"); inner101.say(); Outer10.Inner10 inner10_ = Outer10.getInner10_(); System.out.println("************"); inner10_.say(); } } class Outer10 { //外部类 private int n1 = 10; private static String name = "张三"; private static void cry() {} //Inner10就是静态内部类 //1. 放在外部类的成员位置 //2. 使用static 修饰 //3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员 //4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员 //5. 作用域 :同其他的成员,为整个类体 static class Inner10 { private static String name = "123教育"; public void say() { //如果外部类和静态内部类的成员重名时,静态内部类访问的时, //默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员) System.out.println(name + " 外部类name= " + Outer10.name); cry(); } } public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问 Inner10 inner10 = new Inner10(); inner10.say(); } public Inner10 getInner10() { return new Inner10(); } public static Inner10 getInner10_() { return new Inner10(); } } 输出结果: 132教育 外部类name= 张三 132教育 外部类name= 张三 ============ 132教育 外部类name= 张三 ************ 132教育 外部类name= 张三
-
-
自定义枚举类
-
介绍
- 枚举类是一种特殊的类,用于定义一组固定的常量,这些常量通常代表一组相关的、预定义的值,每个值都是该枚举类的一个实例。
-
使用
-
不需要提供setXxx方法,因为枚举对象值通常为只读.
-
对枚举对象/属性使用final+static共同修饰,实现底层优化.
-
枚举对象名通常使用全部大写,常量的命名规范.
-
枚举对象根据需要,也可以有多个属性
-
小结
- 构造器私有化
- 本类内部创建一组对象
- 对外暴露对象(通过为对象添加public final static修饰符)
- 可以提供get方法,但是不要提供set方法
-
-
代码示例
-
package com.hspedu.enum_; public class Enumeration02 { public static void main(String[] args) { System.out.println(Season.AUTUMN); System.out.println(Season.SPRING); } } //演示字定义枚举实现 class Season {//类 private String name; private String desc;//描述 //定义了四个对象, 固定. public static final Season SPRING = new Season("春天", "温暖"); public static final Season WINTER = new Season("冬天", "寒冷"); public static final Season AUTUMN = new Season("秋天", "凉爽"); public static final Season SUMMER = new Season("夏天", "炎热"); //1. 将构造器私有化,目的防止 直接 new //2. 去掉setXxx方法, 防止属性被修改 //3. 在Season 内部,直接创建固定的对象 //4. 优化,可以加入 final 修饰符 private Season(String name, String desc) { this.name = name; this.desc = desc; } public String getName() { return name; } public String getDesc() { return desc; } @Override public String toString() { return "Season{" + "name='" + name + '\'' + ", desc='" + desc + '\'' + '}'; } } //Season{name='秋天', desc='凉爽'} //Season{name='春天', desc='温暖'}
-
-
enum枚举类
-
注意事项
-
当我们使用enum关键字开发一个枚举类是,默认会继承Enum类,而且是一个final类
-
-
传统的 public static final Season2 SPRING = new Season2(“春天”,“温暖“);简化成SPRING(”春天“,”温暖”),这里必须知道,它调用的是哪个构造器
-
如果使用无参构造器创建枚举对象,则实参列表和小括号都可以省略
-
枚举对象必须放在枚举类的行首
-
当有多个枚举对象时,使用","间隔,最后有一个分号结尾
-
package com.hspedu.enum_; public class Enumeration03 { public static void main(String[] args) { System.out.println(Season2.AUTUMN); System.out.println(Season2.SUMMER); } } //演示使用enum关键字来实现枚举类 enum Season2 {//类 //定义了四个对象, 固定. // public static final Season SPRING = new Season("春天", "温暖"); // public static final Season WINTER = new Season("冬天", "寒冷"); // public static final Season AUTUMN = new Season("秋天", "凉爽"); // public static final Season SUMMER = new Season("夏天", "炎热"); //如果使用了enum 来实现枚举类 //1. 使用关键字 enum 替代 class //2. public static final Season SPRING = new Season("春天", "温暖") 直接使用 // SPRING("春天", "温暖") 解读 常量名(实参列表) //3. 如果有多个常量(对象), 使用 ,号间隔即可 //4. 如果使用enum 来实现枚举,要求将定义常量对象,写在前面 //5. 如果我们使用的是无参构造器,创建常量对象,则可以省略 () SPRING("春天", "温暖"), WINTER("冬天", "寒冷"), AUTUMN("秋天", "凉爽"), SUMMER("夏天", "炎热")/*, What()*/; private String name; private String desc;//描述 private Season2() {//无参构造器 } private Season2(String name, String desc) { this.name = name; this.desc = desc; } public String getName() { return name; } public String getDesc() { return desc; } @Override public String toString() { return "Season{" + "name='" + name + '\'' + ", desc='" + desc + '\'' + '}'; } } //Season{name='秋天', desc='凉爽'} //Season{name='夏天', desc='炎热'}
-
-
enum常用方法
-
toString:Enum类已经重写过了,返回的是当前对象 名,子类可以重写该方法,用于返回对象的属性信息
-
name:返回当前对象名(常量名),子类中不能重写
-
ordinal:返回当前对象的位置号,默认从0开始
-
values:返回当前枚举类中所有的常量
-
valueOf:将字符串转换成枚举对象,要求字符串必须 为已有的常量名,否则报异常!
-
compareTo:比较两个枚举常量,比较的就是编号!
-
代码示例
-
package com.hspedu.enum_; /** * @author 韩顺平 * @version 1.0 * 演示Enum类的各种方法的使用 */ public class EnumMethod { public static void main(String[] args) { //使用Season2 枚举类,来演示各种方法 Season2 autumn = Season2.AUTUMN; //输出枚举对象的名字 System.out.println(autumn.name()); //ordinal() 输出的是该枚举对象的次序/编号,从0开始编号 //AUTUMN 枚举对象是第三个,因此输出 2 System.out.println(autumn.ordinal()); //从反编译可以看出 values方法,返回 Season2[] //含有定义的所有枚举对象 Season2[] values = Season2.values(); System.out.println("===遍历取出枚举对象(增强for)===="); for (Season2 season: values) {//增强for循环 System.out.println(season); } //valueOf:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常 //执行流程 //1. 根据你输入的 "AUTUMN" 到 Season2的枚举对象去查找 //2. 如果找到了,就返回,如果没有找到,就报错 Season2 autumn1 = Season2.valueOf("AUTUMN"); System.out.println("autumn1=" + autumn1); System.out.println(autumn == autumn1); //compareTo:比较两个枚举常量,比较的就是编号 //老韩解读 //1. 就是把 Season2.AUTUMN 枚举对象的编号 和 Season2.SUMMER枚举对象的编号比较 //2. 看看结果 /* public final int compareTo(E o) { return self.ordinal - other.ordinal; } Season2.AUTUMN的编号[2] - Season2.SUMMER的编号[3] */ System.out.println(Season2.AUTUMN.compareTo(Season2.SUMMER)); //补充了一个增强for // int[] nums = {1, 2, 9}; // //普通的for循环 // System.out.println("=====普通的for====="); // for (int i = 0; i < nums.length; i++) { // System.out.println(nums[i]); // } // System.out.println("=====增强的for====="); // //执行流程是 依次从nums数组中取出数据,赋给i, 如果取出完毕,则退出for // for(int i : nums) { // System.out.println("i=" + i); // } } } enum Season2 {//类 //定义了四个对象, 固定. // public static final Season SPRING = new Season("春天", "温暖"); // public static final Season WINTER = new Season("冬天", "寒冷"); // public static final Season AUTUMN = new Season("秋天", "凉爽"); // public static final Season SUMMER = new Season("夏天", "炎热"); //如果使用了enum 来实现枚举类 //1. 使用关键字 enum 替代 class //2. public static final Season SPRING = new Season("春天", "温暖") 直接使用 // SPRING("春天", "温暖") 解读 常量名(实参列表) //3. 如果有多个常量(对象), 使用 ,号间隔即可 //4. 如果使用enum 来实现枚举,要求将定义常量对象,写在前面 //5. 如果我们使用的是无参构造器,创建常量对象,则可以省略 () SPRING("春天", "温暖"), WINTER("冬天", "寒冷"), AUTUMN("秋天", "凉爽"), SUMMER("夏天", "炎热")/*, What()*/; private String name; private String desc;//描述 private Season2() {//无参构造器 } private Season2(String name, String desc) { this.name = name; this.desc = desc; } public String getName() { return name; } public String getDesc() { return desc; } @Override public String toString() { return "Season{" + "name='" + name + '\'' + ", desc='" + desc + '\'' + '}'; } } 输出结果: AUTUMN 2 ===遍历取出枚举对象(增强for)==== Season{name='春天', desc='温暖'} Season{name='冬天', desc='寒冷'} Season{name='秋天', desc='凉爽'} Season{name='夏天', desc='炎热'} autumn1=Season{name='秋天', desc='凉爽'} true -1
-
-
注解
-
介绍
- 注解(Annotation)也被称为元数据(Metadata),用于修饰解释 包、类、方法、属性、构造器、局部变量等数据信息。
- 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
- 在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替javaEE旧版中所遗留的繁冗代码和XML配置等。
- 注解可写可不写,但不能多写
-
Annotation介绍
-
使用 Annotation 时要在其前面增加 @ 符号, 并把该 Annotation 当成一个修饰符使用。用于修饰它支持的程序元素
-
三个基本的 Annotation:
- @Override: 限定某个方法,是重写父类方法, 该注解只能用于方法
- @Deprecated: 用于表示某个程序元素(类, 方法等)已过时
- @SuppressWarnings: 抑制编译器警告
-
-
Override代码示例
-
package com.hspedu.annotation_; public class Override_ { public static void main(String[] args) { } } class Father{//父类 public void fly(){ int i = 0; System.out.println("Father fly..."); } public void say(){} } class Son extends Father {//子类 //解读 //1. @Override 注解放在fly方法上,表示子类的fly方法时重写了父类的fly //2. 这里如果没有写 @Override 还是重写了父类fly //3. 如果你写了@Override注解,编译器就会去检查该方法是否真的重写了父类的 // 方法,如果的确重写了,则编译通过,如果没有构成重写,则编译错误 //4. 看看 @Override的定义 // 解读: 如果发现 @interface 表示一个 注解类 /* @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { } */ @Override //说明 public void fly() { System.out.println("Son fly...."); } @Override public void say() {} } //编译成功
-
-
Deprecated代码示例
-
package com.hspedu.annotation_; public class Deprecated_ { public static void main(String[] args) { A a = new A(); a.hi(); System.out.println(a.n1); } } //解读 //1. @Deprecated 修饰某个元素, 表示该元素已经过时 //2. 即不在推荐使用,但是仍然可以使用 //3. 查看 @Deprecated 注解类的源码 //4. 可以修饰方法,类,字段, 包, 参数 等等 //5. @Deprecated 可以做版本升级过渡使用 /* @Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated { } */ @Deprecated class A { @Deprecated public int n1 = 10; @Deprecated public void hi(){ } } //编译成功
-
-
SuppressWarnings代码示例
-
package com.hspedu.annotation_; import java.util.ArrayList; import java.util.List; @SuppressWarnings({"rawtypes", "unchecked", "unused"}) public class SuppressWarnings_ { //解读 //1. 当我们不希望看到这些警告的时候,可以使用 SuppressWarnings注解来抑制警告信息 //2. 在{""} 中,可以写入你希望抑制(不显示)警告信息 //3. 可以指定的警告类型有 // all,抑制所有警告 // boxing,抑制与封装/拆装作业相关的警告 // //cast,抑制与强制转型作业相关的警告 // //dep-ann,抑制与淘汰注释相关的警告 // //deprecation,抑制与淘汰的相关警告 // //fallthrough,抑制与switch陈述式中遗漏break相关的警告 // //finally,抑制与未传回finally区块相关的警告 // //hiding,抑制与隐藏变数的区域变数相关的警告 // //incomplete-switch,抑制与switch陈述式(enum case)中遗漏项目相关的警告 // //javadoc,抑制与javadoc相关的警告 // //nls,抑制与非nls字串文字相关的警告 // //null,抑制与空值分析相关的警告 // //rawtypes,抑制与使用raw类型相关的警告 // //resource,抑制与使用Closeable类型的资源相关的警告 // //restriction,抑制与使用不建议或禁止参照相关的警告 // //serial,抑制与可序列化的类别遗漏serialVersionUID栏位相关的警告 // //static-access,抑制与静态存取不正确相关的警告 // //static-method,抑制与可能宣告为static的方法相关的警告 // //super,抑制与置换方法相关但不含super呼叫的警告 // //synthetic-access,抑制与内部类别的存取未最佳化相关的警告 // //sync-override,抑制因为置换同步方法而遗漏同步化的警告 // //unchecked,抑制与未检查的作业相关的警告 // //unqualified-field-access,抑制与栏位存取不合格相关的警告 // //unused,抑制与未用的程式码及停用的程式码相关的警告 //4. 关于SuppressWarnings 作用范围是和你放置的位置相关 // 比如 @SuppressWarnings放置在 main方法,那么抑制警告的范围就是 main // 通常我们可以放置具体的语句, 方法, 类. //5. 看看 @SuppressWarnings 源码 //(1) 放置的位置就是 TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE //(2) 该注解类有数组 String[] values() 设置一个数组比如 {"rawtypes", "unchecked", "unused"} /* @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); } */ public static void main(String[] args) { List list = new ArrayList(); list.add("jack"); list.add("tom"); list.add("mary"); int i; System.out.println(list.get(1)); } public void f1() { // @SuppressWarnings({"rawtypes"}) List list = new ArrayList(); list.add("jack"); list.add("tom"); list.add("mary"); // @SuppressWarnings({"unused"}) int i; System.out.println(list.get(1)); } } //tom
-
异常
-
概念
-
Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)
-
执行过程中所发生的异常事件可分为两大类
- Error(错误) :Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError[栈溢出]和OOM(out of memory),Error 是严重错误,程序会崩溃。
- Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问,试图读取不存在的文件,网络连接中断等等,Exception分为两大类:运行时异常[程序运行时,发生的异常]和编译时异常[编程时,编译器检查出的异常]。
-
-
异常体系图
-
-
运行时异常
- NullPointerException 空指针异常
- ArithmeticException 数学运算异常
- ArrayIndexOutOfBoundsException 数组下标越界异常
- ClassCastException 类型转换异常
- NumberFormatException 数字格式不正确异常[]
-
编译时异常
- SQLException/操作数据库时,查询表可能发生异常
- IOException/操作文件时,发生的异常
FileNotFoundException/当操作一个不存在的文件时,发生异常 - ClassNotFoundException/加载类,而该类不存在时,异常
- EOFException/操作文件,到文件末尾,发生异常
- IllegalArguementException/参数异常
-
-
异常处理
-
介绍
- 异常处理就是当异常发生时,对异常处理的方式
-
方式
-
try-catch-finally
- Java提供try和catch块来处理异常。try块用于包含可能出错的代码。catch块用于处理try块中发生的异常。可以根据需要在程序中有多个try.catch块。
-
throws
- 如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
- 在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
-
throws与throw区别
-
意义 位置 后面跟的东西 throws 异常处理的一种方式 方法声明处 异常类型 throw 手动生成异常对象的关键字 方法体中 异常对象
-
-
-
try-catch-finally使用细节
-
如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块
-
如果异常没有发生,则顺序执行try的代码块,不会进入到catch.
-
如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等)
则使用如下代码—finally{} -
可以有多个catch语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,比如(Exception 在后,NullPointerException在 前),如果发生异常,只会匹配一个catch
-
代码示例
-
package com.hspedu.try_; public class TryCatchDetail02 { public static void main(String[] args) { //老韩解读 //1.如果try代码块有可能有多个异常 //2.可以使用多个catch 分别捕获不同的异常,相应处理 //3.要求子类异常写在前面,父类异常写在后面 try { Person person = new Person(); //person = null; System.out.println(person.getName());//NullPointerException int n1 = 10; int n2 = 0; int res = n1 / n2;//ArithmeticException } catch (NullPointerException e) { System.out.println("空指针异常=" + e.getMessage()); } catch (ArithmeticException e) { System.out.println("算术异常=" + e.getMessage()); } catch (Exception e) { System.out.println(e.getMessage()); } finally { } } } class Person { private String name = "jack"; public String getName() { return name; } } //jack //算术异常=/ by zero
-
-
-
throws使用细节
-
对于编译异常,程序中必须处理,比如 try—catch 或者 throws
-
对于运行时异常,程序中如果没有处理,默认就是throws的方式处理[举例]
-
子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类型[举例]
-
在throws 过程中,如果有方法 try—catch,就相当于处理异常,就可以不必throws
-
代码示例
-
package com.hspedu.throws_; import java.io.FileInputStream; import java.io.FileNotFoundException; public class ThrowsDetail { public static void main(String[] args) { f2(); } public static void f2() /*throws ArithmeticException*/ { //1.对于编译异常,程序中必须处理,比如 try-catch 或者 throws //2.对于运行时异常,程序中如果没有处理,默认就是throws的方式处理 int n1 = 10; int n2 = 0; // double res = n1 / n2; 会编译报错 } public static void f1() throws FileNotFoundException { //这里大家思考问题 调用f3() 报错 //解读 //1. 因为f3() 方法抛出的是一个编译异常 //2. 即这时,就要f1() 必须处理这个编译异常 //3. 在f1() 中,要么 try-catch-finally ,或者继续throws 这个编译异常 f3(); // 抛出异常 } public static void f3() throws FileNotFoundException { FileInputStream fis = new FileInputStream("d://aa.txt"); } public static void f4() { //解读: //1. 在f4()中调用方法f5() 是OK //2. 原因是f5() 抛出的是运行异常 //3. 而java中,并不要求程序员显示处理,因为有默认处理机制 f5(); } public static void f5() throws ArithmeticException { } } class Father { //父类 public void method() throws RuntimeException { } } class Son extends Father {//子类 //3. 子类重写父类的方法时,对抛出异常的规定:子类重写的方法, // 所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常类型的子类型 //4. 在throws 过程中,如果有方法 try-catch , 就相当于处理异常,就可以不必throws @Override public void method() throws ArithmeticException { } } //编译成功
-
-
-
-
自定义异常
-
概念
- 当程序中出现了某些“错误”,但该错误信息并没有在Throwable子类中描述处理,这个时候可以自己设计异常类,用于描述该错误信息。
-
步骤
- 定义类:自定义异常类名(程序员自己写)继承Exception或RuntimeException
- 如果继承Exception,属于编译异常
- 如果继承RuntimeException,属于运行异常(一般来说,继承RuntimeException)
-
代码示例
-
package com.hspedu.customexception_; public class CustomException { public static void main(String[] args) /*throws AgeException*/ { int age = 180; //要求范围在 18 – 120 之间,否则抛出一个自定义异常 if(!(age >= 18 && age <= 120)) { //这里我们可以通过构造器,设置信息 throw new AgeException("年龄需要在 18~120之间"); } System.out.println("你的年龄范围正确."); } } //自定义一个异常 //老韩解读 //1. 一般情况下,我们自定义异常是继承 RuntimeException //2. 即把自定义异常做成 运行时异常,好处时,我们可以使用默认的处理机制 //3. 即比较方便 class AgeException extends RuntimeException { public AgeException(String message) {//构造器 super(message); } } //运行出错
-
-
包装类
-
介绍
-
- jdk5前的手动装箱和拆箱方式,装箱:基本类型—>包装类型,反之,拆箱
- jdk5以后(含jdk5)的自动装箱和拆箱方式
- 自动装箱底层调用的是valueOf方法,比如Integer.valueOf()
-
代码示例
-
package com.hspedu.wrapper; public class Integer01 { public static void main(String[] args) { //演示int <--> Integer 的装箱和拆箱 //jdk5前是手动装箱和拆箱 //手动装箱 int->Integer int n1 = 100; Integer integer = new Integer(n1); Integer integer1 = Integer.valueOf(n1); //手动拆箱 //Integer -> int int i = integer.intValue(); //jdk5后,就可以自动装箱和自动拆箱 int n2 = 200; //自动装箱 int->Integer Integer integer2 = n2; //底层使用的是 Integer.valueOf(n2) //自动拆箱 Integer->int int n3 = integer2; //底层仍然使用的是 intValue()方法 } } //编译成功
-
-
包装类的常用方法
-
integer和string类型相互转换
-
代码示例
-
package com.hspedu.wrapper; public class WrapperVSString { public static void main(String[] args) { //包装类(Integer)->String Integer i = 100;//自动装箱 //方式1 String str1 = i + ""; //方式2 String str2 = i.toString(); //方式3 String str3 = String.valueOf(i); //String -> 包装类(Integer) String str4 = "12345"; Integer i2 = Integer.parseInt(str4);//使用到自动装箱 Integer i3 = new Integer(str4);//构造器 System.out.println("编译成功"); } } //编译成功
-
-
-
常用方法
-
package com.hspedu.wrapper; public class WrapperMethod { public static void main(String[] args) { System.out.println(Integer.MIN_VALUE); //返回最小值 System.out.println(Integer.MAX_VALUE);//返回最大值 System.out.println(Character.isDigit('a'));//判断是不是数字 System.out.println(Character.isLetter('a'));//判断是不是字母 System.out.println(Character.isUpperCase('a'));//判断是不是大写 System.out.println(Character.isLowerCase('a'));//判断是不是小写 System.out.println(Character.isWhitespace('a'));//判断是不是空格 System.out.println(Character.toUpperCase('a'));//转成大写 System.out.println(Character.toLowerCase('A'));//转成小写 } } 输出结果: -2147483648 2147483647 false true false true false A a
-
-
integer类
-
package com.hspedu.wrapper; public class WrapperExercise03 { public static void main(String[] args) { //示例一 Integer i1 = new Integer(127); Integer i2 = new Integer(127); System.out.println(i1 == i2);//F //示例二 Integer i3 = new Integer(128); Integer i4 = new Integer(128); System.out.println(i3 == i4);//F //示例三 Integer i5 = 127;//底层Integer.valueOf(127) Integer i6 = 127;//-128~127 System.out.println(i5 == i6); //T //示例四 Integer i7 = 128; Integer i8 = 128; System.out.println(i7 == i8);//F //示例五 Integer i9 = 127; //Integer.valueOf(127) Integer i10 = new Integer(127); System.out.println(i9 == i10);//F //示例六 Integer i11=127; int i12=127; //只有有基本数据类型,判断的是 //值是否相同 System.out.println(i11==i12); //T //示例七 Integer i13=128; int i14=128; System.out.println(i13==i14);//T } } 输出结果: false false true false false true true
-
-
大数处理方案
-
BigInteger和BigDecimal常见方法
-
bigdecimal示例
package com.hspedu.bignum; import java.math.BigDecimal; public class BigDecimal_ { public static void main(String[] args) { //当我们需要保存一个精度很高的数时,double 不够用 //可以是 BigDecimal // double d = 1999.11111111111999999999999977788d; // System.out.println(d); BigDecimal bigDecimal = new BigDecimal("1999.11"); BigDecimal bigDecimal2 = new BigDecimal("3"); System.out.println(bigDecimal); //老韩解读 //1. 如果对 BigDecimal进行运算,比如加减乘除,需要使用对应的方法 //2. 创建一个需要操作的 BigDecimal 然后调用相应的方法即可 System.out.println(bigDecimal.add(bigDecimal2)); System.out.println(bigDecimal.subtract(bigDecimal2)); System.out.println(bigDecimal.multiply(bigDecimal2)); //System.out.println(bigDecimal.divide(bigDecimal2));//可能抛出异常ArithmeticException //在调用divide 方法时,指定精度即可. BigDecimal.ROUND_CEILING //如果有无限循环小数,就会保留 分子 的精度 System.out.println(bigDecimal.divide(bigDecimal2, BigDecimal.ROUND_CEILING)); } } 输出结果: 1999.11 2002.11 1996.11 5997.33 666.37
-
biginteger示例
package com.hspedu.bignum; import java.math.BigInteger; public class BigInteger_ { public static void main(String[] args) { //当我们编程中,需要处理很大的整数,long 不够用 //可以使用BigInteger的类来搞定 // long l = 23788888899999999999999999999l; // System.out.println("l=" + l); BigInteger bigInteger = new BigInteger("23788888899999999999999999999"); BigInteger bigInteger2 = new BigInteger("10099999999999999999999999999999999999999999999999999999999999999999999999999999999"); System.out.println(bigInteger); //老韩解读 //1. 在对 BigInteger 进行加减乘除的时候,需要使用对应的方法,不能直接进行 + - * / //2. 可以创建一个 要操作的 BigInteger 然后进行相应操作 BigInteger add = bigInteger.add(bigInteger2); System.out.println(add);// BigInteger subtract = bigInteger.subtract(bigInteger2); System.out.println(subtract);//减 BigInteger multiply = bigInteger.multiply(bigInteger2); System.out.println(multiply);//乘 BigInteger divide = bigInteger.divide(bigInteger2); System.out.println(divide);//除 } } 输出结果: 23788888899999999999999999999 10100000000000000000000000000000000000000000000000000023788888899999999999999999998 -10099999999999999999999999999999999999999999999999999976211111100000000000000000000 240267777889999999999999999989899999999999999999999999999999999999999999999999999976211111100000000000000000001 0
-
-
-
-
String类
-
介绍
-
String 对象用于保存字符串,也就是一组字符序列
-
字符串常量对象是用双引号括起的字符序列。例如:“你好”、“12.97”、“boy”等
-
字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节。
-
String类较常用构造器:
- String s1 new String0;//
- String s2 new String(String original);
- String s3 new String(char[] a);
- String s4 new String(char[] a, int startlndex, int count)
-
-
-
代码示例
-
package com.hspedu.string_; public class String01 { public static void main(String[] args) { //1.String 对象用于保存字符串,也就是一组字符序列 //2. "jack" 字符串常量, 双引号括起的字符序列 //3. 字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节 //4. String 类有很多构造器,构造器的重载 // 常用的有 String s1 = new String(); // //String s2 = new String(String original); //String s3 = new String(char[] a); //String s4 = new String(char[] a,int startIndex,int count) //String s5 = new String(byte[] b) //5. String 类实现了接口 Serializable【String 可以串行化:可以在网络传输】 // 接口 Comparable [String 对象可以比较大小] //6. String 是final 类,不能被其他的类继承 //7. String 有属性 private final char value[]; 用于存放字符串内容 //8. 一定要注意:value 是一个final类型, 不可以修改(需要功力):即value不能指向 // 新的地址,但是单个字符内容是可以变化 String name = "jack"; name = "tom"; final char[] value = {'a','b','c'}; char[] v2 = {'t','o','m'}; value[0] = 'H'; //value = v2; 不可以修改 value地址 } } //编译成功
-
-
创建对象方式
-
方式一:直接赋值 String s=“hspedu”;
-
方式二:调用构造器 String s= new String(“hspedu”);
-
区别
- 方式一:先从常量池查看是否有“hsp”数据空间,如果有,直接指向;如果没有则重新创建,然后指向。s最终指向的是常量池的空间地址
- 方式二:先在堆中创建空间,里面维护了value属性,指向常量池的hsp空间如果常量池没有“hsp”,重新创建,如果有,直接通过value指向。最终指向的是堆中的空间地址。
-
体系图
-
-
代码示例
-
package com.hspedu.string_; public class StringExercise04 { public static void main(String[] args) { String s1 = "hspedu"; //指向常量池”hspedu” String s2 = "java"; //指向常量池”java” String s4 = "java";//指向常量池”java” String s3 = new String("java");//指向堆中对象 System.out.println(s2 == s3); // F System.out.println(s2 == s4); //T System.out.println(s2.equals(s3));//T System.out.println(s1 == s2); //F System.out.println(s2 == s3.intern());//T } }
-
-
-
常见方法
- equals//区分大小写,判断内容是否相等
- equalslgnoreCase/忽略大小写的判断内容是否相等 length/获取字符的个数,字符串的长度
- indexOf/获取字符在字符串中第1次出现的索引,索引从0开始,如果找不到,返回—1
- lastlndexOf/获取字符在字符串中最后1次出现的索引,索引从0开始,如找不到,返回—
- 1substring/截取指定范围的子串
- trim/去前后空格
- charAt:获取某索引处的字符,注意不能使用Str[index]这种方式.
-
代码示例
-
package com.hspedu.string_; public class StringMethod01 { public static void main(String[] args) { //1. equals 前面已经讲过了. 比较内容是否相同,区分大小写 String str1 = "hello"; String str2 = "Hello"; System.out.println(str1.equals(str2));// // 2.equalsIgnoreCase 忽略大小写的判断内容是否相等 String username = "johN"; if ("john".equalsIgnoreCase(username)) { System.out.println("Success!"); } else { System.out.println("Failure!"); } // 3.length 获取字符的个数,字符串的长度 System.out.println("123".length()); // 4.indexOf 获取字符在字符串对象中第一次出现的索引,索引从0开始,如果找不到,返回-1 String s1 = "wer@terwe@g"; int index = s1.indexOf('@'); System.out.println(index);// 3 System.out.println("weIndex=" + s1.indexOf("we"));//0 // 5.lastIndexOf 获取字符在字符串中最后一次出现的索引,索引从0开始,如果找不到,返回-1 s1 = "wer@terwe@g@"; index = s1.lastIndexOf('@'); System.out.println(index);//11 System.out.println("ter的位置=" + s1.lastIndexOf("ter"));//4 // 6.substring 截取指定范围的子串 String name = "hello,张三"; //下面name.substring(6) 从索引6开始截取后面所有的内容 System.out.println(name.substring(6));//截取后面的字符 //name.substring(0,5)表示从索引0开始截取,截取到索引 5-1=4位置 System.out.println(name.substring(2,5));//llo } } 输出结果: false Success! 3 3 weIndex=0 11 ter的位置=4 张三 llo
-
package com.hspedu.string_; public class StringMethod02 { public static void main(String[] args) { // 1.toUpperCase转换成大写 String s = "heLLo"; System.out.println(s.toUpperCase());//HELLO // 2.toLowerCase System.out.println(s.toLowerCase());//hello // 3.concat拼接字符串 String s1 = "宝玉"; s1 = s1.concat("林黛玉").concat("薛宝钗").concat("together"); System.out.println(s1);//宝玉林黛玉薛宝钗together // 4.replace 替换字符串中的字符 s1 = "宝玉 and 林黛玉 林黛玉 林黛玉"; //在s1中,将 所有的 林黛玉 替换成薛宝钗 // 老韩解读: s1.replace() 方法执行后,返回的结果才是替换过的. // 注意对 s1没有任何影响 String s11 = s1.replace("宝玉", "jack"); System.out.println(s1);//宝玉 and 林黛玉 林黛玉 林黛玉 System.out.println(s11);//jack and 林黛玉 林黛玉 林黛玉 // 5.split 分割字符串, 对于某些分割字符,我们需要 转义比如 | \\等 String poem = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦"; //老韩解读: // 1. 以 , 为标准对 poem 进行分割 , 返回一个数组 // 2. 在对字符串进行分割时,如果有特殊字符,需要加入 转义符 \ String[] split = poem.split(","); poem = "E:\\aaa\\bbb"; split = poem.split("\\\\"); System.out.println("==分割后内容==="); for (int i = 0; i < split.length; i++) { System.out.println(split[i]); } // 6.toCharArray 转换成字符数组 s = "happy"; char[] chs = s.toCharArray(); for (int i = 0; i < chs.length; i++) { System.out.println(chs[i]); } // 7.compareTo 比较两个字符串的大小,如果前者大, // 则返回正数,后者大,则返回负数,如果相等,返回0 // 老韩解读 // (1) 如果长度相同,并且每个字符也相同,就返回 0 // (2) 如果长度相同或者不相同,但是在进行比较时,可以区分大小 // 就返回 if (c1 != c2) { // return c1 - c2; // } // (3) 如果前面的部分都相同,就返回 str1.len - str2.len String a = "jcck";// len = 3 String b = "jack";// len = 4 System.out.println(a.compareTo(b)); // 返回值是 'c' - 'a' = 2的值 // 8.format 格式字符串 /* 占位符有: * %s 字符串 %c 字符 %d 整型 %.2f 浮点型 * */ String name = "john"; int age = 10; double score = 56.857; char gender = '男'; //将所有的信息都拼接在一个字符串. String info = "我的姓名是" + name + "年龄是" + age + ",成绩是" + score + "性别是" + gender + "。希望大家喜欢我!"; System.out.println(info); //老韩解读 //1. %s , %d , %.2f %c 称为占位符 //2. 这些占位符由后面变量来替换 //3. %s 表示后面由 字符串来替换 //4. %d 是整数来替换 //5. %.2f 表示使用小数来替换,替换后,只会保留小数点两位, 并且进行四舍五入的处理 //6. %c 使用char 类型来替换 String formatStr = "我的姓名是%s 年龄是%d,成绩是%.2f 性别是%c.希望大家喜欢我!"; String info2 = String.format(formatStr, name, age, score, gender); System.out.println("info2=" + info2); } } 输出结果: E: aaa bbb h a p p y 2 我的姓名是john年龄是10,成绩是56.857性别是男。希望大家喜欢我! info2=我的姓名是john 年龄是10,成绩是56.86 性别是男.希望大家喜欢我!
-
-
-
StringBuffer类
-
介绍
- java.lang.StringBuffer代表可变的字符序列,可以对字符串内容进行增删
- 很多方法与String相同,但StringBuffer是可变长度的。
- StringBuffer是一个容器。
-
与string类的区别
- String保存的是字符串常量,里面的值不能更改,每次String类的更新实际上就是更改地址,效率较低/private final char value[];
- StringBuffer保存的是字符串变量,里面的值可以更改,每次
StringBuffer的更新实际上可以更新内容,不用每次更新地址,效率较高/char[]value;/这个放在堆.
-
string与stringbuffer的转换
-
代码示例
-
package com.hspedu.stringbuffer_; public class StringAndStringBuffer { public static void main(String[] args) { //看 String——>StringBuffer String str = "hello tom"; //方式1 使用构造器 //注意: 返回的才是StringBuffer对象,对str 本身没有影响 StringBuffer stringBuffer = new StringBuffer(str); //方式2 使用的是append方法 StringBuffer stringBuffer1 = new StringBuffer(); stringBuffer1 = stringBuffer1.append(str); //看看 StringBuffer ->String StringBuffer stringBuffer3 = new StringBuffer("CongSec教育"); //方式1 使用StringBuffer提供的 toString方法 String s = stringBuffer3.toString(); //方式2: 使用构造器来搞定 String s1 = new String(stringBuffer3); } } //编译成功
-
-
-
常用方法
-
append():
- 用于在StringBuffer对象末尾添加内容
- 可以连续调用,支持多种数据类型
-
delete(int start, int end):
- 删除指定范围内的字符
- 删除的范围是[start, end),包括start但不包括end
-
replace(int start, int end, String str):
- 使用给定的字符串替换指定范围内的字符
- 替换的范围是[start, end)
-
indexOf(String str):
- 查找指定子串第一次出现的索引
- 如果找不到,返回-1
-
insert(int offset, String str):
- 在指定位置插入字符串
- 原来该位置及之后的内容会自动后移
-
length():
- 返回StringBuffer对象的长度
-
代码示例
-
package com.hspedu.stringbuffer_; public class StringBufferMethod { public static void main(String[] args) { StringBuffer s = new StringBuffer("hello"); //增 s.append(',');// "hello," s.append("张三丰");//"hello,张三丰" s.append("赵敏").append(100).append(true).append(10.5);//"hello,张三丰赵敏100true10.5" System.out.println(s);//"hello,张三丰赵敏100true10.5" //删 /* * 删除索引为>=start && <end 处的字符 * 解读: 删除 11~14的字符 [11, 14) */ s.delete(11, 14); System.out.println(s);//"hello,张三丰赵敏true10.5" //改 //老韩解读,使用 周芷若 替换 索引9-11的字符 [9,11) s.replace(9, 11, "周芷若"); System.out.println(s);//"hello,张三丰周芷若true10.5" //查找指定的子串在字符串第一次出现的索引,如果找不到返回-1 int indexOf = s.indexOf("张三丰"); System.out.println(indexOf);//6 //插 //老韩解读,在索引为9的位置插入 "赵敏",原来索引为9的内容自动后移 s.insert(9, "赵敏"); System.out.println(s);//"hello,张三丰赵敏周芷若true10.5" //长度 System.out.println(s.length());//22 System.out.println(s); } }
-
-
-
-
StringBuilder类
-
介绍
- 一个可变的字符序列。此类提供一个与StringBuffer 兼容的API,但不保证同步(StringBuilder 不是线程安全)。该类被设计用作 StringBuffer的一个简易 替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先采用该类因为在大多数实现中,它比 StringBuffer 要快[后面测]。
- 在StringBuilder 上的主要操作是 append 和 insert 方法,可重载这些方法, 以接受任意类型的数据。
-
常用方法
- StringBuilder 和 StringBuffer 均代表可变的字符序列,方法是一样的,所 以使用和StringBuffer一样,看老师演示.[参考StringBuffer].
- StringBuilder 是final
- 继承了AbstractStringBuilder, 属性char[] value,内容存到 value
- 实现了Serializable接口,序列化 (所谓序列化即可以保存类型和数据本
-
string,stringbuffer,stringbuilder三者的区别和原则
-
区别
- StringBuilder 和 StringBuffer 非常类似,均代表可变的字符序列,而且方法
也一样 - String:不可变字符序列,效率低,但是复用率高。
- StringBuffer:可变字符序列、效率较高(增删)、线程安全,看源码
- StringBuilder:可变字符序列、效率最高、线程不安全
- String使用注意说明:
string s=“a”;/创建了一个字符串
s+=“b”;//实际上原来的“a”字符串对象已经丢弃了,现在又产生了一个字符串s+“b”(也就是“ab”)。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能=>结论:如果我们对String 做大量修改,不要使用String
- StringBuilder 和 StringBuffer 非常类似,均代表可变的字符序列,而且方法
-
原则
- 如果字符串存在大量的修改操作,一般使用 StringBuffer 或StringBuilde
- 如果字符串存在大量的修改操作,并在单线程的情况,使用StringBuilder
- 如果字符串存在大量的修改操作,并在多线程的情况,使用StringBuffer
- 如果我们字符串很少修改,被多个对象引用,使用String,比如配置信息等
-
-
Math类
-
介绍
- Math类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数
-
常用方法
-
Math.abs(int a)
- 功能:求绝对值
-
Math.pow(double base, double exponent)
- 功能:求幂
-
Math.ceil(double a)
- 功能:向上取整,返回大于或等于参数的最小整数(double类型)
-
Math.floor(double a)
- 功能:向下取整,返回小于或等于参数的最大整数(double类型)
-
Math.round(double a)
- 功能:四舍五入
-
Math.sqrt(double a)
- 功能:求平方根
-
Math.random()
- 功能:返回0到1之间的随机小数(包括0,不包括1)
-
Math.min(int a, int b)
- 功能:返回两个数中的最小值
-
Math.max(int a, int b)
- 功能:返回两个数中的最大值
-
-
代码示例
-
package com.hspedu.math_; public class MathMethod { public static void main(String[] args) { //看看Math常用的方法(静态方法) //1.abs 绝对值 int abs = Math.abs(-9); System.out.println(abs);//9 //2.pow 求幂 double pow = Math.pow(2, 4);//2的4次方 System.out.println(pow);//16 //3.ceil 向上取整,返回>=该参数的最小整数(转成double); double ceil = Math.ceil(3.9); System.out.println(ceil);//4.0 //4.floor 向下取整,返回<=该参数的最大整数(转成double) double floor = Math.floor(4.001); System.out.println(floor);//4.0 //5.round 四舍五入 Math.floor(该参数+0.5) long round = Math.round(5.51); System.out.println(round);//6 //6.sqrt 求开方 double sqrt = Math.sqrt(9.0); System.out.println(sqrt);//3.0 //7.random 求随机数 // random 返回的是 0 <= x < 1 之间的一个随机小数 // 思考:请写出获取 a-b之间的一个随机整数,a,b均为整数 ,比如 a = 2, b=7 // 即返回一个数 x 2 <= x <= 7 // 老韩解读 Math.random() * (b-a) 返回的就是 0 <= 数 <= b-a // (1) (int)(a) <= x <= (int)(a + Math.random() * (b-a +1) ) // (2) 使用具体的数给小伙伴介绍 a = 2 b = 7 // (int)(a + Math.random() * (b-a +1) ) = (int)( 2 + Math.random()*6) // Math.random()*6 返回的是 0 <= x < 6 小数 // 2 + Math.random()*6 返回的就是 2<= x < 8 小数 // (int)(2 + Math.random()*6) = 2 <= x <= 7 // (3) 公式就是 (int)(a + Math.random() * (b-a +1) ) for(int i = 0; i < 100; i++) { System.out.println((int)(2 + Math.random() * (7 - 2 + 1))); } //max , min 返回最大值和最小值 int min = Math.min(1, 9); int max = Math.max(45, 90); System.out.println("min=" + min); System.out.println("max=" + max); } } 输出结果: 9 16.0 4.0 4.0 6 3.0 3 6 6 7 4 4 5 5 3 4 4 3 3 4 4 3 3 3 4 7 6 3 2 6 4 2 3 3 4 7 5 6 3 6 7 6 7 4 2 3 2 4 7 5 5 3 3 5 6 5 6 4 2 3 5 6 7 5 4 7 2 3 5 6 3 4 7 6 7 5 3 3 3 5 5 3 6 4 2 6 4 6 2 6 2 3 5 7 5 7 2 3 5 3 2 4 2 4 5 4 min=1 max=90
-
Arrays类
-
常用方法
- toString 返回数组的字符串形式
- sort 排序(自然排序和定制排序)
- binarySearch 通过二分搜索法进行查找,要求必须排好序
- copyOf 数组元素的复制
- fill 数组元素的填充
- equals 比较两个数组元素内容是否完全一致
- asList 将一组值,转换成list
-
代码示例
-
package com.hspedu.arrays_; import java.util.Arrays; import java.util.List; public class ArraysMethod02 { public static void main(String[] args) { Integer[] arr = {1, 2, 90, 123, 567}; System.out.println(Arrays.toString(arr)); // binarySearch 通过二分搜索法进行查找,要求必须排好 // 解读 //1. 使用 binarySearch 二叉查找 //2. 要求该数组是有序的. 如果该数组是无序的,不能使用binarySearch //3. 如果数组中不存在该元素,就返回 return -(low + 1); // key not found. int index = Arrays.binarySearch(arr, 567); System.out.println("index=" + index); //copyOf 数组元素的复制 // 解读 //1. 从 arr 数组中,拷贝 arr.length个元素到 newArr数组中 //2. 如果拷贝的长度 > arr.length 就在新数组的后面 增加 null //3. 如果拷贝长度 < 0 就抛出异常NegativeArraySizeException //4. 该方法的底层使用的是 System.arraycopy() Integer[] newArr = Arrays.copyOf(arr, arr.length); System.out.println("==拷贝执行完毕后=="); System.out.println(Arrays.toString(newArr)); //ill 数组元素的填充 Integer[] num = new Integer[]{9,3,2}; //解读 //1. 使用 99 去填充 num数组,可以理解成是替换原理的元素 Arrays.fill(num, 99); System.out.println("==num数组填充后=="); System.out.println(Arrays.toString(num)); //equals 比较两个数组元素内容是否完全一致 Integer[] arr2 = {1, 2, 90, 123}; //解读 //1. 如果arr 和 arr2 数组的元素一样,则方法true; //2. 如果不是完全一样,就返回 false boolean equals = Arrays.equals(arr, arr2); System.out.println("equals=" + equals); //asList 将一组值,转换成list //解读 //1. asList方法,会将 (2,3,4,5,6,1)数据转成一个List集合 //2. 返回的 asList 编译类型 List(接口) //3. asList 运行类型 java.util.Arrays#ArrayList, 是Arrays类的 // 静态内部类 private static class ArrayList<E> extends AbstractList<E> // implements RandomAccess, java.io.Serializable List asList = Arrays.asList(2,3,4,5,6,1); System.out.println("asList=" + asList); System.out.println("asList的运行类型" + asList.getClass()); } } 输出结果: [1, 2, 90, 123, 567] index=4 ==拷贝执行完毕后== [1, 2, 90, 123, 567] ==num数组填充后== [99, 99, 99] equals=false asList=[2, 3, 4, 5, 6, 1] asList的运行类型class java.util.Arrays$ArrayList
-
System类
-
常用方法
- exit 退出当前程序
- arraycopy: 复制数组元素,比较适合底层调用,一般使用Arrays.copyOf完成复制数组.
- currentTimeMillens: 返回当前时间距离1970-1-1的毫秒数
- gc: 运行垃圾回收机制System.gc();
-
代码示例
-
package com.hspedu.system_; import java.util.Arrays; public class System_ { public static void main(String[] args) { //exit 退出当前程序 // System.out.println("ok1"); // //解读 // //1. exit(0) 表示程序退出 // //2. 0 表示一个状态 , 正常的状态 // System.exit(0);// // System.out.println("ok2"); //arraycopy :复制数组元素,比较适合底层调用, // 一般使用Arrays.copyOf完成复制数组 int[] src={1,2,3}; int[] dest = new int[3];// dest 当前是 {0,0,0} //解读 //1. 主要是搞清楚这五个参数的含义 //2. // 源数组 // * @param src the source array. // srcPos: 从源数组的哪个索引位置开始拷贝 // * @param srcPos starting position in the source array. // dest : 目标数组,即把源数组的数据拷贝到哪个数组 // * @param dest the destination array. // destPos: 把源数组的数据拷贝到 目标数组的哪个索引 // * @param destPos starting position in the destination data. // length: 从源数组拷贝多少个数据到目标数组 // * @param length the number of array elements to be copied. System.arraycopy(src, 0, dest, 0, src.length); // int[] src={1,2,3}; System.out.println("dest=" + Arrays.toString(dest));//[1, 2, 3] //currentTimeMillens:返回当前时间距离1970-1-1 的毫秒数 // 解读: System.out.println(System.currentTimeMillis()); } } //dest=[1, 2, 3] //1728893605219
-
日期类
-
第一代日期类–Date
-
介绍
- Date:精确到毫秒,代表特定的瞬间,类是在java.util包
- SimpleDateFormat:格式和解析日期的类 SimpleDateFormat格式化和解析日期的具 体类。它允许进行格式化(日期—>文本)、解析(文本—>日期)和规范化.
-
代码示例
-
package com.hspedu.date_; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class Date01 { public static void main(String[] args) throws ParseException { //解读 //1. 获取当前系统时间 //2. 这里的Date 类是在java.util包 //3. 默认输出的日期格式是国外的方式, 因此通常需要对格式进行转换 Date d1 = new Date(); //获取当前系统时间 System.out.println("当前日期=" + d1); Date d2 = new Date(9234567); //通过指定毫秒数得到时间 System.out.println("d2=" + d2); //获取某个时间对应的毫秒数 // //解读 //1. 创建 SimpleDateFormat对象,可以指定相应的格式 //2. 这里的格式使用的字母是规定好,不能乱写 SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E"); String format = sdf.format(d1); // format:将日期转换成指定格式的字符串 System.out.println("当前日期=" + format); //解读 //1. 可以把一个格式化的String 转成对应的 Date //2. 得到Date 仍然在输出时,还是按照国外的形式,如果希望指定格式输出,需要转换 //3. 在把String -> Date , 使用的 sdf 格式需要和你给的String的格式一样,否则会抛出转换异常 String s = "1996年01月01日 10:20:30 星期一"; Date parse = sdf.parse(s); System.out.println("parse=" + sdf.format(parse)); } } 输出结果: 当前日期=Mon Oct 14 16:31:15 CST 2024 d2=Thu Jan 01 10:33:54 CST 1970 当前日期=2024年10月14日 04:31:15 星期一 parse=1996年01月01日 10:20:30 星期一
-
-
-
第二代日期类–Calendar
-
介绍
- 第二代日期类,主要就是 Calendar类(日历)。
public abstract class Calendar extends Object implements Serializable,
Cloneable, Comparable - Calendar类是一个抽象类,它为特定瞬间与一组诸如YEAR、MONTH、DAY_OFMONTH、HOUR 等 日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。
- 第二代日期类,主要就是 Calendar类(日历)。
-
代码示例
-
package com.hspedu.date_; import java.util.Calendar; public class Calendar_ { public static void main(String[] args) { //解读 //1. Calendar是一个抽象类, 并且构造器是private //2. 可以通过 getInstance() 来获取实例 //3. 提供大量的方法和字段提供给程序员 //4. Calendar没有提供对应的格式化的类,因此需要程序员自己组合来输出(灵活) //5. 如果我们需要按照 24小时进制来获取时间, Calendar.HOUR ==改成=> Calendar.HOUR_OF_DAY Calendar c = Calendar.getInstance(); //创建日历类对象//比较简单,自由 System.out.println("c=" + c); //2.获取日历对象的某个日历字段 System.out.println("年:" + c.get(Calendar.YEAR)); // 这里为什么要 + 1, 因为Calendar 返回月时候,是按照 0 开始编号 System.out.println("月:" + (c.get(Calendar.MONTH) + 1)); System.out.println("日:" + c.get(Calendar.DAY_OF_MONTH)); System.out.println("小时:" + c.get(Calendar.HOUR)); System.out.println("分钟:" + c.get(Calendar.MINUTE)); System.out.println("秒:" + c.get(Calendar.SECOND)); //Calender 没有专门的格式化方法,所以需要程序员自己来组合显示 System.out.println(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH) + 1) + "-" + c.get(Calendar.DAY_OF_MONTH) + " " + c.get(Calendar.HOUR_OF_DAY) + ":" + c.get(Calendar.MINUTE) + ":" + c.get(Calendar.SECOND) ); } } 输出结果: c=java.util.GregorianCalendar[time=1728895290907,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=31,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2024,MONTH=9,WEEK_OF_YEAR=42,WEEK_OF_MONTH=3,DAY_OF_MONTH=14,DAY_OF_YEAR=288,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=2,AM_PM=1,HOUR=4,HOUR_OF_DAY=16,MINUTE=41,SECOND=30,MILLISECOND=907,ZONE_OFFSET=28800000,DST_OFFSET=0] 年:2024 月:10 日:14 小时:4 分钟:41 秒:30 2024-10-14 16:41:30
-
-
-
第三代日期类
-
基本使用
-
代码示例
-
package com.hspedu.date_; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collection; public class LocalDate_ { public static void main(String[] args) { //第三代日期 //解读 //1. 使用now() 返回表示当前日期时间的 对象 LocalDateTime ldt = LocalDateTime.now(); //LocalDate.now();//LocalTime.now() System.out.println(ldt); //2. 使用DateTimeFormatter 对象来进行格式化 // 创建 DateTimeFormatter对象 DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String format = dateTimeFormatter.format(ldt); System.out.println("格式化的日期=" + format); System.out.println("年=" + ldt.getYear()); System.out.println("月=" + ldt.getMonth()); System.out.println("月=" + ldt.getMonthValue()); System.out.println("日=" + ldt.getDayOfMonth()); System.out.println("时=" + ldt.getHour()); System.out.println("分=" + ldt.getMinute()); System.out.println("秒=" + ldt.getSecond()); LocalDate now = LocalDate.now(); //可以获取年月日 LocalTime now2 = LocalTime.now();//获取到时分秒 //提供 plus 和 minus方法可以对当前时间进行加或者减 //看看890天后,是什么时候 把 年月日-时分秒 LocalDateTime localDateTime = ldt.plusDays(890); System.out.println("890天后=" + dateTimeFormatter.format(localDateTime)); //看看在 3456分钟前是什么时候,把 年月日-时分秒输出 LocalDateTime localDateTime2 = ldt.minusMinutes(3456); System.out.println("3456分钟前 日期=" + dateTimeFormatter.format(localDateTime2)); } } 输出结果: 2024-10-14T17:14:56.487 格式化的日期=2024-10-14 17:14:56 年=2024 月=OCTOBER 月=10 日=14 时=17 分=14 秒=56 890天后=2027-03-23 17:14:56 3456分钟前 日期=2024-10-12 07:38:56
-
-
-
instant时间戳
-
package com.hspedu.date_; import java.time.Instant; import java.util.Date; public class Instant_ { public static void main(String[] args) { //1.通过 静态方法 now() 获取表示当前时间戳的对象 Instant now = Instant.now(); System.out.println(now); //2. 通过 from 可以把 Instant转成 Date Date date = Date.from(now); //3. 通过 date的toInstant() 可以把 date 转成Instant对象 Instant instant = date.toInstant(); } } //2024-10-14T09:28:02.798Z
-
-
集合
-
介绍
- 可以动态保存任意多个对象,使用比较方便!
- 提供了一系列方便的操作对象的方法:add、remove、set、get等
- 使用集合添加,删除新元素的示意代码—简洁了
-
框架体系图
-
-
-
Collection的常用接口和方法
-
Collection的接口的特点
- collection实现子类可以存放多个元素,每个元素可以是Object
- 有些Collection的实现类,可以存放重复的元素,有些不可以
- 有些Collection的实现类,有些是有序的(List),有些不是有序(Set)
- Collection接口没有直接的实现子类,是通过它的子接口Set和List来
实现的
-
常用方法
-
add(Object element)
: 添加单个元素到集合中 -
remove(Object element)
: 从集合中删除指定元素 -
contains(Object element)
: 检查集合是否包含指定元素 -
size()
: 获取集合中元素的数量 -
isEmpty()
: 检查集合是否为空 -
clear()
: 清空集合中的所有元素 -
addAll(Collection c)
: 将另一个集合中的所有元素添加到当前集合 -
containsAll(Collection c)
: 检查当前集合是否包含另一个集合的所有元素 -
removeAll(Collection c)
: 从当前集合中删除另一个集合中存在的所有元素 -
代码示例
-
package com.hspedu.collection_; import java.util.ArrayList; import java.util.List; public class CollectionMethod { @SuppressWarnings({"all"}) public static void main(String[] args) { List list = new ArrayList(); // add:添加单个元素 list.add("jack"); list.add(10);//list.add(new Integer(10)) list.add(true); System.out.println("list=" + list); // remove:删除指定元素 //list.remove(0);//删除第一个元素 list.remove(true);//指定删除某个元素 System.out.println("list=" + list); // contains:查找元素是否存在 System.out.println(list.contains("jack"));//T // size:获取元素个数 System.out.println(list.size());//2 // isEmpty:判断是否为空 System.out.println(list.isEmpty());//F // clear:清空 list.clear(); System.out.println("list=" + list); // addAll:添加多个元素 ArrayList list2 = new ArrayList(); list2.add("红楼梦"); list2.add("三国演义"); list.addAll(list2); System.out.println("list=" + list); // containsAll:查找多个元素是否都存在 System.out.println(list.containsAll(list2));//T // removeAll:删除多个元素 list.add("聊斋"); list.removeAll(list2); System.out.println("list=" + list);//[聊斋] // 说明:以ArrayList实现类来演示. } } 输出结果: list=[jack, 10, true] list=[jack, 10] true 2 false list=[] list=[红楼梦, 三国演义] true list=[聊斋]
-
-
-
接口遍历元素方式1–使用Iterator(迭代器)
-
介绍
- Iterator对象称为迭代器,主要用于遍历 Collection集合中的元素。
- 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了lterator接口的对象,即可以返回一个迭代器。
- Iterator 的结构.[看一张图]
- Iterator 仅用于遍历集合,Iterator 本身并不存放对象。
-
代码示例
-
package com.hspedu.collection_; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class CollectionIterator { @SuppressWarnings({"all"}) public static void main(String[] args) { Collection col = new ArrayList(); col.add(new Book("三国演义", "罗贯中", 10.1)); col.add(new Book("小李飞刀", "古龙", 5.1)); col.add(new Book("红楼梦", "曹雪芹", 34.6)); //System.out.println("col=" + col); //现在老师希望能够遍历 col集合 //1. 先得到 col 对应的 迭代器 Iterator iterator = col.iterator(); //2. 使用while循环遍历 // while (iterator.hasNext()) {//判断是否还有数据 // //返回下一个元素,类型是Object // Object obj = iterator.next(); // System.out.println("obj=" + obj); // } //老师教大家一个快捷键,快速生成 while => itit //显示所有的快捷键的的快捷键 ctrl + j while (iterator.hasNext()) { Object obj = iterator.next(); System.out.println("obj=" + obj); } //3. 当退出while循环后 , 这时iterator迭代器,指向最后的元素 // iterator.next();//NoSuchElementException //4. 如果希望再次遍历,需要重置我们的迭代器 iterator = col.iterator(); System.out.println("===第二次遍历==="); while (iterator.hasNext()) { Object obj = iterator.next(); System.out.println("obj=" + obj); } } } class Book { private String name; private String author; private double price; public Book(String name, String author, double price) { this.name = name; this.author = author; this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } @Override public String toString() { return "Book{" + "name='" + name + '\'' + ", author='" + author + '\'' + ", price=" + price + '}'; } } 输出结果: obj=Book{name='三国演义', author='罗贯中', price=10.1} obj=Book{name='小李飞刀', author='古龙', price=5.1} obj=Book{name='红楼梦', author='曹雪芹', price=34.6} ===第二次遍历=== obj=Book{name='三国演义', author='罗贯中', price=10.1} obj=Book{name='小李飞刀', author='古龙', price=5.1} obj=Book{name='红楼梦', author='曹雪芹', price=34.6}
-
-
-
接口遍历元素方式2–for循环增强
-
介绍
- 增强for循环,可以代替iterator迭代器
-
特点
- 增强for就是简化版的iterator, 本质一样。只能用于遍历集合或数组。
-
基本语法
- for(元素类型 元素名 : 集合名或数组名){
访问元素
}
- for(元素类型 元素名 : 集合名或数组名){
-
代码示例
-
1.创建3个Dog {name,age}对象,放入到 ArrayList中,赋给 List引用
2.用迭代器和增强for循环两种方式来遍历
3.重写Dog 的toString方法,输出name和age-
package com.hspedu.collection_; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class CollectionExercise { @SuppressWarnings({"all"}) public static void main(String[] args) { List list = new ArrayList(); list.add(new Dog("小黑", 3)); list.add(new Dog("大黄", 100)); list.add(new Dog("大壮", 8)); //先使用for增强 for (Object dog : list) { System.out.println("dog=" + dog); } //使用迭代器 System.out.println("===使用迭代器来遍历==="); Iterator iterator = list.iterator(); while (iterator.hasNext()) { Object dog = iterator.next(); System.out.println("dog=" + dog); } } } /** * 创建 3个 Dog {name, age} 对象,放入到 ArrayList 中,赋给 List 引用 * 用迭代器和增强for循环两种方式来遍历 * 重写Dog 的toString方法, 输出name和age */ class Dog { private String name; private int age; public Dog(String name, int age) { this.name = name; this.age = 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; } @Override public String toString() { return "Dog{" + "name='" + name + '\'' + ", age=" + age + '}'; } } 输出结果: dog=Dog{name='小黑', age=3} dog=Dog{name='大黄', age=100} dog=Dog{name='大壮', age=8} ===使用迭代器来遍历=== dog=Dog{name='小黑', age=3} dog=Dog{name='大黄', age=100} dog=Dog{name='大壮', age=8}
-
-
-
-
-
List接口和常用方法
-
介绍
- List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复
- List集合中的每个元素都有其对应的顺序索引,即支持索引。
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
-
常用方法
* `add(Object ele)`: 在列表末尾添加元素 * `add(int index, Object ele)`: 在指定索引位置插入元素 * `addAll(int index, Collection eles)`: 从指定索引位置开始,添加另一个集合的所有元素 * `get(int index)`: 获取指定索引位置的元素(示例中提到但未展示) * `indexOf(Object obj)`: 返回指定元素在列表中第一次出现的索引 * `lastIndexOf(Object obj)`: 返回指定元素在列表中最后一次出现的索引 * `remove(int index)`: 移除指定索引位置的元素,并返回该元素 * `set(int index, Object ele)`: 将指定索引位置的元素替换为新元素 * `subList(int fromIndex, int toIndex)`: 返回列表中指定范围的子列表(包含 fromIndex,不包含 toIndex) * ###### 代码示例 * ```java package com.hspedu.list_; import java.util.ArrayList; import java.util.List; public class ListMethod { @SuppressWarnings({"all"}) public static void main(String[] args) { List list = new ArrayList(); list.add("张三丰"); list.add("贾宝玉"); // void add(int index, Object ele):在index位置插入ele元素 //在index = 1的位置插入一个对象 list.add(1, "韩顺平"); System.out.println("list=" + list); // boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来 List list2 = new ArrayList(); list2.add("jack"); list2.add("tom"); list.addAll(1, list2); System.out.println("list=" + list); // Object get(int index):获取指定index位置的元素 //说过 // int indexOf(Object obj):返回obj在集合中首次出现的位置 System.out.println(list.indexOf("tom"));//2 // int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置 list.add("韩顺平"); System.out.println("list=" + list); System.out.println(list.lastIndexOf("韩顺平")); // Object remove(int index):移除指定index位置的元素,并返回此元素 list.remove(0); System.out.println("list=" + list); // Object set(int index, Object ele):设置指定index位置的元素为ele , 相当于是替换. list.set(1, "玛丽"); System.out.println("list=" + list); // List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合 // 注意返回的子集合 fromIndex <= subList < toIndex List returnlist = list.subList(0, 2); System.out.println("returnlist=" + returnlist); } } 输出结果: list=[张三丰, 韩顺平, 贾宝玉] list=[张三丰, jack, tom, 韩顺平, 贾宝玉] 2 list=[张三丰, jack, tom, 韩顺平, 贾宝玉, 韩顺平] 5 list=[jack, tom, 韩顺平, 贾宝玉, 韩顺平] list=[jack, 玛丽, 韩顺平, 贾宝玉, 韩顺平] returnlist=[jack, 玛丽] ```
-
三种遍历方式[ArrayList,LinkedList,Vector]
-
代码示例
-
package com.hspedu.list_; import java.util.*; public class ListFor { @SuppressWarnings({"all"}) public static void main(String[] args) { //List 接口的实现子类 Vector LinkedList //List list = new ArrayList(); //List list = new Vector(); List list = new LinkedList(); list.add("jack"); list.add("tom"); list.add("鱼香肉丝"); list.add("北京烤鸭子"); //遍历 //1. 迭代器 Iterator iterator = list.iterator(); while (iterator.hasNext()) { Object obj = iterator.next(); System.out.println(obj); } System.out.println("=====增强for====="); //2. 增强for for (Object o : list) { System.out.println("o=" + o); } System.out.println("=====普通for===="); //3. 使用普通for for (int i = 0; i < list.size(); i++) { System.out.println("对象=" + list.get(i)); } } } 输出结果: jack tom 鱼香肉丝 北京烤鸭子 =====增强for===== o=jack o=tom o=鱼香肉丝 o=北京烤鸭子 =====普通for==== 对象=jack 对象=tom 对象=鱼香肉丝 对象=北京烤鸭子
-
-
-
代码示例
-
-
package com.hspedu.list_; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Vector; @SuppressWarnings({"all"}) public class ListExercise02 { public static void main(String[] args) { //List list = new ArrayList(); List list = new LinkedList(); //List list = new Vector(); list.add(new Book("红楼梦", "曹雪芹", 100)); list.add(new Book("西游记", "吴承恩", 10)); list.add(new Book("水浒传", "施耐庵", 19)); list.add(new Book("三国", "罗贯中", 80)); //list.add(new Book("西游记", "吴承恩", 10)); //如何对集合进行排序 //遍历 for (Object o : list) { System.out.println(o); } //冒泡排序 sort(list); System.out.println("==排序后=="); for (Object o : list) { System.out.println(o); } } //静态方法 //价格要求是从小到大 public static void sort(List list) { int listSize = list.size(); for (int i = 0; i < listSize - 1; i++) { for (int j = 0; j < listSize - 1 - i; j++) { //取出对象Book Book book1 = (Book) list.get(j); Book book2 = (Book) list.get(j + 1); if (book1.getPrice() > book2.getPrice()) {//交换 list.set(j, book2); list.set(j + 1, book1); } } } } } 输出结果: 名称:红楼梦 价格:100.0 作者:曹雪芹 名称:西游记 价格:10.0 作者:吴承恩 名称:水浒传 价格:19.0 作者:施耐庵 名称:三国 价格:80.0 作者:罗贯中 ==排序后== 名称:西游记 价格:10.0 作者:吴承恩 名称:水浒传 价格:19.0 作者:施耐庵 名称:三国 价格:80.0 作者:罗贯中 名称:红楼梦 价格:100.0 作者:曹雪芹
-
-
-
-
各节点的底层架构和原理跳过(516)
泛型
-
介绍
- 理解:泛(广泛)型(类型)=>Integer, String,Dog
1)泛型又称参数化类型,是Jdk5.0出现的新特性,解决数据类型的安全性问题2)在类声明或实例化时只要指定好需要的具体的类型即可。
3)Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮
4)泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。[有点难,举例Generic03.java]
- 理解:泛(广泛)型(类型)=>Integer, String,Dog
-
代码示例
-
package com.hspedu.generic; import java.util.List; public class Generic03 { public static void main(String[] args) { //注意,特别强调: E具体的数据类型在定义Person对象的时候指定,即在编译期间,就确定E是什么类型 Person<String> person = new Person<String>("韩顺平教育"); person.show(); //String /* 你可以这样理解,上面的Person类 class Person { String s ;//E表示 s的数据类型, 该数据类型在定义Person对象的时候指定,即在编译期间,就确定E是什么类型 public Person(String s) {//E也可以是参数类型 this.s = s; } public String f() {//返回类型使用E return s; } } */ Person<Integer> person2 = new Person<Integer>(100); person2.show();//Integer /* class Person { Integer s ;//E表示 s的数据类型, 该数据类型在定义Person对象的时候指定,即在编译期间,就确定E是什么类型 public Person(Integer s) {//E也可以是参数类型 this.s = s; } public Integer f() {//返回类型使用E return s; } } */ } } //泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型, // 或者是某个方法的返回值的类型,或者是参数类型 class Person<E> { E s ;//E表示 s的数据类型, 该数据类型在定义Person对象的时候指定,即在编译期间,就确定E是什么类型 public Person(E s) {//E也可以是参数类型 this.s = s; } public E f() {//返回类型使用E return s; } public void show() { System.out.println(s.getClass());//显示s的运行类型 } } //class java.lang.String //class java.lang.Integer
-
-
语法
-
代码示例
-
package com.hspedu.generic; import java.util.*; @SuppressWarnings({"all"}) public class GenericExercise { public static void main(String[] args) { //使用泛型方式给HashSet 放入3个学生对象 HashSet<Student> students = new HashSet<Student>(); students.add(new Student("jack", 18)); students.add(new Student("tom", 28)); students.add(new Student("mary", 19)); //遍历 for (Student student : students) { System.out.println(student); } //使用泛型方式给HashMap 放入3个学生对象 //K -> String V->Student HashMap<String, Student> hm = new HashMap<String, Student>(); /* public class HashMap<K,V> {} */ hm.put("milan", new Student("milan", 38)); hm.put("smith", new Student("smith", 48)); hm.put("hsp", new Student("hsp", 28)); //迭代器 EntrySet /* public Set<Map.Entry<K,V>> entrySet() { Set<Map.Entry<K,V>> es; return (es = entrySet) == null ? (entrySet = new EntrySet()) : es; } */ Set<Map.Entry<String, Student>> entries = hm.entrySet(); /* public final Iterator<Map.Entry<K,V>> iterator() { return new EntryIterator(); } */ Iterator<Map.Entry<String, Student>> iterator = entries.iterator(); System.out.println("=============================="); while (iterator.hasNext()) { Map.Entry<String, Student> next = iterator.next(); System.out.println(next.getKey() + "-" + next.getValue()); } } } /** * 创建 3个学生对象 * 放入到HashSet中学生对象, 使用. * 放入到 HashMap中,要求 Key 是 String name, Value 就是 学生对象 * 使用两种方式遍历 */ class Student { private String name; private int age; public Student(String name, int age) { this.name = name; this.age = 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; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } } 输出结果: Student{name='tom', age=28} Student{name='jack', age=18} Student{name='mary', age=19} ============================== smith-Student{name='smith', age=48} hsp-Student{name='hsp', age=28} milan-Student{name='milan', age=38}
-
-
使用细节
-
interface List{},public class HashSet{}.等等 说明:T,E只能是引用类型
看看下面语句是否正确?:
List list new ArrayList0; //OK
List list2 = new ArrayList();//错误 -
在给泛型指定具体类型后,可以传入该类型或者其子类类型
-
泛型使用形式
List list1 new ArrayList(); List list2 = new ArrayList<>();[说明:] -
如果我们这样写List list3=new ArrayList();默认给它的泛型是[E就是Object] 即:
-
代码示例
-
package com.hspedu.generic; import java.util.ArrayList; import java.util.List; @SuppressWarnings({"all"}) public class GenericDetail { public static void main(String[] args) { //1.给泛型指向数据类型是,要求是引用类型,不能是基本数据类型 List<Integer> list = new ArrayList<Integer>(); //OK //List<int> list2 = new ArrayList<int>();//错误 //2. 说明 //因为 E 指定了 A 类型, 构造器传入了 new A() //在给泛型指定具体类型后,可以传入该类型或者其子类类型 Pig<A> aPig = new Pig<A>(new A()); aPig.f(); Pig<A> aPig2 = new Pig<A>(new B()); aPig2.f(); //3. 泛型的使用形式 ArrayList<Integer> list1 = new ArrayList<Integer>(); List<Integer> list2 = new ArrayList<Integer>(); //在实际开发中,我们往往简写 //编译器会进行类型推断, 老师推荐使用下面写法 ArrayList<Integer> list3 = new ArrayList<>(); List<Integer> list4 = new ArrayList<>(); ArrayList<Pig> pigs = new ArrayList<>(); //4. 如果是这样写 泛型默认是 Object ArrayList arrayList = new ArrayList();//等价 ArrayList<Object> arrayList = new ArrayList<Object>(); /* public boolean add(Object e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } */ Tiger tiger = new Tiger(); /* class Tiger {//类 Object e; public Tiger() {} public Tiger(Object e) { this.e = e; } } */ } } class Tiger<E> {//类 E e; public Tiger() {} public Tiger(E e) { this.e = e; } } class A {} class B extends A {} class Pig<E> {// E e; public Pig(E e) { this.e = e; } public void f() { System.out.println(e.getClass()); //运行类型 } } //class com.hspedu.generic.A //class com.hspedu.generic.B
-
-
-
代码示例*
-
要求:
定义Employee类
1)该类包含:private成员变量name,sal,birthday,其中 birthday 为 MyDate类的对 象;
2)为每一个属性定义getter, setter 方法; 3)重写toString 方法输出 name, sal, birthday
4)MyDate类包含:private成员变量month,day,year;并为每一个属性定义 getter, setter 方法;
5)创建该类的3个对象,并把这些对象放入ArrayList 集合中(ArrayList 需使用泛型来定义),对集合中的元素进行排序,并遍历输出:
排序方式:调用ArrayList的sort方法,传入Comparator对象[使用泛型],先按照 name排序,如果name相同,则按生日日期的先后排序。【即:定制排序】 -
Employee.java: package com.hspedu.generic; public class Employee { private String name; private double sal; private MyDate birthday; public Employee(String name, double sal, MyDate birthday) { this.name = name; this.sal = sal; this.birthday = birthday; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getSal() { return sal; } public void setSal(double sal) { this.sal = sal; } public MyDate getBirthday() { return birthday; } public void setBirthday(MyDate birthday) { this.birthday = birthday; } @Override public String toString() { return "\nEmployee{" + "name='" + name + '\'' + ", sal=" + sal + ", birthday=" + birthday + '}'; } } ========================================================================= MyDate.java: package com.hspedu.generic; public class MyDate implements Comparable<MyDate>{ private int year; private int month; private int day; public MyDate(int year, int month, int day) { this.year = year; this.month = month; this.day = day; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public int getDay() { return day; } public void setDay(int day) { this.day = day; } @Override public String toString() { return "MyDate{" + "year=" + year + ", month=" + month + ", day=" + day + '}'; } @Override public int compareTo(MyDate o) { //把对year-month-day比较放在这里 int yearMinus = year - o.getYear(); if(yearMinus != 0) { return yearMinus; } //如果year相同,就比较month int monthMinus = month - o.getMonth(); if(monthMinus != 0) { return monthMinus; } //如果year 和 month return day - o.getDay(); } } ========================================================================= GenericExercise02.java: package com.hspedu.generic; import java.util.ArrayList; import java.util.Comparator; @SuppressWarnings({"all"}) public class GenericExercise02 { public static void main(String[] args) { ArrayList<Employee> employees = new ArrayList<>(); employees.add(new Employee("tom", 20000, new MyDate(1980,12,11))); employees.add(new Employee("jack", 12000, new MyDate(2001,12,12))); employees.add(new Employee("tom", 50000, new MyDate(1980,12,10))); System.out.println("employees=" + employees); employees.sort(new Comparator<Employee>() { @Override public int compare(Employee emp1, Employee emp2) { //先按照name排序,如果name相同,则按生日日期的先后排序。【即:定制排序】 //先对传入的参数进行验证 if(!(emp1 instanceof Employee && emp2 instanceof Employee)) { System.out.println("类型不正确.."); return 0; } //比较name int i = emp1.getName().compareTo(emp2.getName()); if(i != 0) { return i; } //下面是对birthday的比较,因此,我们最好把这个比较,放在MyDate类完成 //封装后,将来可维护性和复用性,就大大增强. return emp1.getBirthday().compareTo(emp2.getBirthday()); } }); System.out.println("==对雇员进行排序=="); System.out.println(employees); } } /** * 定义Employee类 * 1) 该类包含:private成员变量name,sal,birthday,其中 birthday 为 MyDate 类的对象; * 2) 为每一个属性定义 getter, setter 方法; * 3) 重写 toString 方法输出 name, sal, birthday * 4) MyDate类包含: private成员变量month,day,year;并为每一个属性定义 getter, setter 方法; * 5) 创建该类的 3 个对象,并把这些对象放入 ArrayList 集合中(ArrayList 需使用泛型来定义),对集合中的元素进行排序,并遍历输出: * * 排序方式: 调用ArrayList 的 sort 方法 , * 传入 Comparator对象[使用泛型],先按照name排序,如果name相同,则按生日日期的先后排序。【即:定制排序】 * 有一定难度 15min , 比较经典 泛型使用案例 GenericExercise02.java */ 输出结果: employees=[ Employee{name='tom', sal=20000.0, birthday=MyDate{year=1980, month=12, day=11}}, Employee{name='jack', sal=12000.0, birthday=MyDate{year=2001, month=12, day=12}}, Employee{name='tom', sal=50000.0, birthday=MyDate{year=1980, month=12, day=10}}] ==对雇员进行排序== [ Employee{name='jack', sal=12000.0, birthday=MyDate{year=2001, month=12, day=12}}, Employee{name='tom', sal=50000.0, birthday=MyDate{year=1980, month=12, day=10}}, Employee{name='tom', sal=20000.0, birthday=MyDate{year=1980, month=12, day=11}}]
-
思考:
-
这里的主要精彩之处在于将比较年龄放在MyDate类里和sort方法中的两个conpareTo方法(142和149行)是不一样的,接下来我会讲下为什么能放入以及为什么不一样
-
首先我们在Employee类中可以看到name的返回类型为string,而birthday的返回类型为Mydate
-
在Mydate类中MyDate声明中实现
Comparable
接口并对compareTo
方法进行重写并使用MyDate泛型 -
当编辑器运行到
compareTo
方法时看到getName
类型为String时就会运行到内部的方法,看到getBirthday
类型为MayDate时就会运行到MyDate方法里-
-
-
-
自定义泛型
-
自定义泛型类
-
语法
- class 类名 <T,R…>{//…表示可以有多个泛型
成员
}
- class 类名 <T,R…>{//…表示可以有多个泛型
-
注意细节
- 普通成员可以使用泛型(属性、方法)
- 使用泛型的数组,不能初始化
- 静态方法中不能使用类的泛型
- 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
- 如果在创建对象时,没有指定类型,默认为Object
-
代码示例
-
package com.hspedu.customgeneric; import java.util.Arrays; @SuppressWarnings({"all"}) public class CustomGeneric_ { public static void main(String[] args) { //T=Double, R=String, M=Integer Tiger<Double,String,Integer> g = new Tiger<>("john"); g.setT(10.9); //OK //g.setT("yy"); //错误,类型不对 System.out.println(g); Tiger g2 = new Tiger("john~~");//OK T=Object R=Object M=Object g2.setT("yy"); //OK ,因为 T=Object "yy"=String 是Object子类 System.out.println("g2=" + g2); } } //解读 //1. Tiger 后面泛型,所以我们把 Tiger 就称为自定义泛型类 //2, T, R, M 泛型的标识符, 一般是单个大写字母 //3. 泛型标识符可以有多个. //4. 普通成员可以使用泛型 (属性、方法) //5. 使用泛型的数组,不能初始化 //6. 静态方法中不能使用类的泛型 class Tiger<T, R, M> { String name; R r; //属性使用到泛型 M m; T t; //因为数组在new 不能确定T的类型,就无法在内存开空间 T[] ts; public Tiger(String name) { this.name = name; } public Tiger(R r, M m, T t) {//构造器使用泛型 this.r = r; this.m = m; this.t = t; } public Tiger(String name, R r, M m, T t) {//构造器使用泛型 this.name = name; this.r = r; this.m = m; this.t = t; } //因为静态是和类相关的,在类加载时,对象还没有创建 //所以,如果静态方法和静态属性使用了泛型,JVM就无法完成初始化 // static R r2; // public static void m1(M m) { // // } //方法使用泛型 public String getName() { return name; } public void setName(String name) { this.name = name; } public R getR() { return r; } public void setR(R r) {//方法使用到泛型 this.r = r; } public M getM() {//返回类型可以使用泛型. return m; } public void setM(M m) { this.m = m; } public T getT() { return t; } public void setT(T t) { this.t = t; } @Override public String toString() { return "Tiger{" + "name='" + name + '\'' + ", r=" + r + ", m=" + m + ", t=" + t + ", ts=" + Arrays.toString(ts) + '}'; } } //Tiger{name='john', r=null, m=null, t=10.9, ts=null} //g2=Tiger{name='john~~', r=null, m=null, t=yy, ts=null}
-
-
-
自定义泛型接口
-
基本语法
- interface 接口名 <T,R…>{
}
- interface 接口名 <T,R…>{
-
注意细节
- 接口中,静态成员也不能使用泛型(这个和泛型类规定一样)
- 泛型接口的类型,在继承接口或者实现接口时确定
- 没有指定类型,默认为Object
-
代码示例
-
package com.hspedu.customgeneric; public class CustomInterfaceGeneric { public static void main(String[] args) { } } /** * 泛型接口使用的说明 * 1. 接口中,静态成员也不能使用泛型 * 2. 泛型接口的类型, 在继承接口或者实现接口时确定 * 3. 没有指定类型,默认为Object */ //在继承接口 指定泛型接口的类型 interface IA extends IUsb<String, Double> { } //当我们去实现IA接口时,因为IA在继承IUsu 接口时,指定了U 为String R为Double //,在实现IUsu接口的方法时,使用String替换U, 是Double替换R class AA implements IA { @Override public Double get(String s) { return null; } @Override public void hi(Double aDouble) { } @Override public void run(Double r1, Double r2, String u1, String u2) { } } //实现接口时,直接指定泛型接口的类型 //给U 指定Integer 给 R 指定了 Float //所以,当我们实现IUsb方法时,会使用Integer替换U, 使用Float替换R class BB implements IUsb<Integer, Float> { @Override public Float get(Integer integer) { return null; } @Override public void hi(Float aFloat) { } @Override public void run(Float r1, Float r2, Integer u1, Integer u2) { } } //没有指定类型,默认为Object //建议直接写成 IUsb<Object,Object> class CC implements IUsb { //等价 class CC implements IUsb<Object,Object> { @Override public Object get(Object o) { return null; } @Override public void hi(Object o) { } @Override public void run(Object r1, Object r2, Object u1, Object u2) { } } interface IUsb<U, R> { int n = 10; //U name; 不能这样使用 //普通方法中,可以使用接口泛型 R get(U u); void hi(R r); void run(R r1, R r2, U u1, U u2); //在jdk8 中,可以在接口中,使用默认方法, 也是可以使用泛型 default R method(U u) { return null; } } //编译成功
-
-
-
自定义泛型方法
-
基本语法
- 修饰符 <T,R…> 返回类型 方法名(参数列表){
}
- 修饰符 <T,R…> 返回类型 方法名(参数列表){
-
使用细节
- 泛型方法,可以定义在普通类中,也可以定义在泛型类中
- 当泛型方法被调用时,类型会确定
- public void eat(E e){},修饰符后没有<T,R…> eat方法不是泛型方法,而是使用了泛型
-
代码示例
-
package com.hspedu.customgeneric; import java.util.ArrayList; @SuppressWarnings({"all"}) public class CustomMethodGeneric { public static void main(String[] args) { Car car = new Car(); car.fly("宝马", 100);//当调用方法时,传入参数,编译器,就会确定类型 System.out.println("======="); car.fly(300, 100.1);//当调用方法时,传入参数,编译器,就会确定类型 //测试 //T->String, R-> ArrayList Fish<String, ArrayList> fish = new Fish<>(); fish.hello(new ArrayList(), 11.3f); } } //泛型方法,可以定义在普通类中, 也可以定义在泛型类中 class Car {//普通类 public void run() {//普通方法 } //说明 泛型方法 //1. <T,R> 就是泛型 //2. 是提供给 fly使用的 public <T, R> void fly(T t, R r) {//泛型方法 System.out.println(t.getClass());//String System.out.println(r.getClass());//Integer } } class Fish<T, R> {//泛型类 public void run() {//普通方法 } public<U,M> void eat(U u, M m) {//泛型方法 } //说明 //1. 下面hi方法不是泛型方法 //2. 是hi方法使用了类声明的 泛型 public void hi(T t) { } //泛型方法,可以使用类声明的泛型,也可以使用自己声明泛型 public<K> void hello(R r, K k) { System.out.println(r.getClass());//ArrayList System.out.println(k.getClass());//Float } } 输出结果: class java.lang.String class java.lang.Integer ======= class java.lang.Integer class java.lang.Double class java.util.ArrayList class java.lang.Float
-
-
-
泛型通配符
-
介绍
- 泛型不具备继承性
- <?>:支持任意泛型类型
- <? extends A>:支持A类以及A类的子类,规定了泛型的上限
- <?super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限
-
代码示例
-
package com.hspedu; import java.util.ArrayList; import java.util.List; public class GenericExtends { public static void main(String[] args) { Object o = new String("xx"); //泛型没有继承性 //List<Object> list = new ArrayList<String>(); //举例说明下面三个方法的使用 List<Object> list1 = new ArrayList<>(); List<String> list2 = new ArrayList<>(); List<AA> list3 = new ArrayList<>(); List<BB> list4 = new ArrayList<>(); List<CC> list5 = new ArrayList<>(); //如果是 List<?> c ,可以接受任意的泛型类型 printCollection1(list1); printCollection1(list2); printCollection1(list3); printCollection1(list4); printCollection1(list5); //List<? extends AA> c: 表示 上限,可以接受 AA或者AA子类 // printCollection2(list1);//× // printCollection2(list2);//× printCollection2(list3);//√ printCollection2(list4);//√ printCollection2(list5);//√ //List<? super AA> c: 支持AA类以及AA类的父类,不限于直接父类 printCollection3(list1);//√ //printCollection3(list2);//× printCollection3(list3);//√ //printCollection3(list4);//× //printCollection3(list5);//× //冒泡排序 //插入排序 //.... } // ? extends AA 表示 上限,可以接受 AA或者AA子类 public static void printCollection2(List<? extends AA> c) { for (Object object : c) { System.out.println(object); } } //说明: List<?> 表示 任意的泛型类型都可以接受 public static void printCollection1(List<?> c) { for (Object object : c) { // 通配符,取出时,就是Object System.out.println(object); } } // ? super 子类类名AA:支持AA类以及AA类的父类,不限于直接父类, //规定了泛型的下限 public static void printCollection3(List<? super AA> c) { for (Object object : c) { System.out.println(object); } } } class AA { } class BB extends AA { } class CC extends BB { } //编译成功
-
-