定义 : 是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么 接口的内部主要就是封装了方法,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法 (JDK 9);它与定义类方式相似, 但是使用interface 关键字, 它也会被编译成class 文件, 但一定要明确它不是一个类, 而是一种引用数据类型; 接口的使用, 它不能创建对象, 但是可以被实现(implements , 类似于被继承); 一个实现接口的类(可以看做是接口的子类), 需要实现接口中所有的抽象方法, 创建该类的对象, 就可以调用方法了, 否则它必须是一个抽象类
引用数据类型 : 数组, 类, 接口
定义格式 :
public interface 接口名称 {
// 抽象方法
// 默认方法
// 静态方法
// 私有方法
}
含有抽象方法
public interface InterFaceName{
public abstract void method();
}
含有默认方法和静态方法
默认方法 : 使用default修饰, default不可省略, 供子类调用或者子类重写
静态方法 : 使用static修饰, 供接口直接调用
public interface InterFaceName{
public default void method(){
// 执行语句
}
public static void method(){
// 执行语句
}
}
含有私有方法
public interface InterFaceName{
private void method(){
// 执行语句
}
}
基本实现
类与接口的关系为实现关系, 即类实现接口, 该类可以称为接口的实现类, 也可称为接口的子类; 实现的动作类似基础 , 格式相仿, 只是关键字不同, 实现使用 implements 关键字
非抽象子类实现接口
class 类名 implements 接口名{
// 重写接口中抽象方法[必须]
// 重写接口中默人方法[可选]
}
// 抽象方法的使用
// 定义接口
public interface LiveAble {
// 定义抽象方法
public abstract void eat();
public abstract void sleep();
}
// 定义实现类
public class Animal implements LiveAble {
@Override
public void eat() {
System.out.println("吃东西");
}
@Override
public void sleep() {
System.out.println("晚上睡");
}
}
// 实现测试类
public class InterfaceDemo {
public static void main(String[] args) {
// 创建子类对象
Animal a = new Animal();
// 调用实现后的方法
a.eat();
a.sleep();
}
}
// 默认方法的使用
可以继承, 可以重写, 二选一, 但是只能通过实现类的对象来调用
// 定义接口
public interface LiveAble{
public default void fly(){
System.out.println("天上飞");
}
}
// 定义实现类
public class Animal implements LiveAble{
// 继承什么都不写, 直接调用
}
// 定义测试类
public class InterfaceDemo{
public static void main(String[] args){
// 创建子类对象
Animal a = new Animal();
// 调用默认方法
a.fly();
}
}
// 重写默认方法
// 定义接口
public interface LiveAble{
public default void fly(){
System.out.println("天上飞");
}
}
// 定义实现类
public class Animal implements LiveAble{
@Overridepublic
public default void fly(){
System.out.println("自由自在地飞");
}
}
// 定义测试类
public class InterfaceDemo{
public static void main(String[] args){
// 创建子类对象
Animal a = new Animal();
// 调用默认方法
a.fly();
}
}
// 静态方法的使用
静态与 .class 文件相关, 只能使用接口名调用,不可以通过实现类名或者类的对象调用
// 定义接口
public interface LiveAble{
public static void run(){
System.out.println("跑起来!!!");
}
}
// 定义实现类
public class Animal implements LiveAble{
// 无法重写静态方法
}
// 定义测试类
public class InterfaceDemo{
public static void main(String[] args){
Animal.run() // 错误,无法继承,也无法调用
LiveAble.run();
}
}
// 私有方法的使用
私有方法 : 只有默认方法可以调用
私有静态方法 : 默认方法和静态方法可以调用
如果一个接口中有多个默认方法, 并且方法中有重复的内容, 那么可以抽取出来, 封装到私有方法中, 供默认方法去调用; 从设计的角度讲, 私有的方法是对默认方法和静态方法的辅助
// 定义接口
public interface LiveAble{
default void func(){
// 在默认方法当中调用私有方法, 实现类不可调用私有方法
func1();
func2();
}
private void func1(){
System.out.println("跑起来!!!");
}
private void func2(){
System.out.println("跑起来!!!");
}
}
接口中的常量和使用
接口当中也可以定义"成员变量", 但是必须使用public static final 三个关键字进行修饰,从效果上看,这就是[常量], 一旦使用final关键字进行修饰,说明不可变;
格式 : public static final 数据类型 常量名称 = 数据值;
注意事项:
接口的多实现
在继承体系中 , 一个类只能继承一个父类; 而对于接口而言, 一个类是可以实现多个接口的, 这叫做接口的多实现; 并且, 一个类能在继承一个父类的同时, 实现多个接口的基础
class 类名 [extends 父类名] implements 接口名1,接口名2,接口名3{
// 重写接口中抽象方法[必须]
// 重写接口中默认方法[不重名名可选]
// []: 可选操作
}
// 抽象方法
接口当中, 有多个抽象方法时 , 实现类必须重写所有抽象方法,; 如果抽象方法有重名的, 只需要重写一次
// 定义多个接口
interface A{
public abstract void showA();
public abstract void show();
}
interface B{
public abstract void showB();
public abstract void show();
}
// 定义实现类
public class C implements A,B{
@Override
public void showA{
System.out.println("showA");
}
@Override
public void showB{
System.out.println("showB");
}
@Override
public void show{
System.out.println("show"); //在实现类当中,只重写一次接口当中重名的方法
}
}
// 默认方法
接口中, 有多个默认方法时, 实现类都可继承使用; 如果默认方法有重名的, 必须重写一次
// 定义多个接口
interface A{
public default void methodA();
public default void method();
}
interface B{
public default void methodB();
public default void method();
}
// 定义实现类
public class C implements A,B{
@Override
public void method{
System.out.println("method");
}
}
// 静态方法
接口中, 存在同名的静态方法并不会冲突, 原因是只能通过各自的接口名访问静态方法; 不能通过实现类访问
优先级问题
当一个类, 既继承一个父类, 又实现若干个接口时, 父类中的成员方法与接口中的默认方法重名, 此时子类就近执行父类的成员方法
接口中, 无法定义成员变量, 但是可以定义成员常量, 其值不可改变, 默认使用 public static final 修饰
接口中, 没有构造方法, 不能创建对象; 也没有静态代码块
多态是继封装, 继承之后, 面向对象的第三大特性; extends或者implements实现, 是多态性的前提
多态 : 是指同一行为, 具有多个不同表现形式; 代码的体现 --> 父类引用指向子类对象
多态的体现在多态的格式 : 父类名 对象名 = new 子类名 实例名.方法()
父类名 : 指子类对象继承的父类类型, 或者实现的父接口类型
Fu f = new Zi();
f.method();
当使用多态方式调用方法时, 首先检查父类中是否有该方法, 如果没有,则编译错误; 如果有, 执行的是子类重写后的方法
// 定义父类
public abstract class Animal{
public abstratc void eat();
}
// 定义子类
class Cat extends Animal{
public void eat(){
System.out.println("吃鱼");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("吃骨头");
}
}
// 定义测试类
public class Test{
public static void main(String args){
// 多态形式, 创建多些
Animal a1 = new Cat();
a1.eat(); // 调用的是 Cat的eat()
Animal a2 = new Dog();
a2.eat(); // 调用的是 Dog的eat()
}
}
使用多态的好处
无论右边new 的时候, 换成那个子类对象, 等号左边调用方法都不会变化
引用类型转换
// 向上转型
多态本身是子类类型向父类类型向上转换的过程, 这个过程是默认的当父类引用指向一个子类对象时, 便是向上转型, 它一定是安全的; 但是它无法调用子类原本的内容
使用格式 : 父类类型 变量名 = new 子类类型(); Animal a = new Cat();
// 向下转型
父类类型向子类类型向下转换的过程,是强制的; 一个已经向上转型的子类对象, 将父类引用转为子类引用, 可以使用强制类型转换的格式, 便是向下转型
使用格式 : 子类类型 变量名 = (子类类型) 父类变量名; Cat a = (Cat) a;
转型的异常 : cat 向上转型为 Animal, dog 向上转型为 Animal; cat再次向下转型的时候, 要防止不能转型成dog; 需要使用 instance of 关键字, 给引用变量做类型的校验 ; 对象名 instanceof 类名称
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat();// 调用的是 Cat 的 eat
// 向下转型;判断父类引用a本来是不是dog
if (a instanceof Cat){
c = (Cat)a;
c.catchMouse();// 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse();// 调用的是 Dog 的 watchHouse
}
}
}
final关键字 : 用于修饰不可改变的内容, 可以用于修饰类, 方法, 和变量
对于类, 方法来说 ; abstract和final关键字不能同时存在
类 : 被修饰的类, 不能被继承; 即不能有子类
方法 : 被修饰的方法, 不能被重写
变量 : 被修饰的变量 , 不能被重新赋值; 只能有唯一的一次赋值
// 对于基本类型来说 , 不可变说的是变量当中的数据不可变
// 对于引用类型来说 , 不可变说的是变量当中的地址值不可变
// 由于成员变量有默认值, 所以用了final之后必须手动赋值, 不会 再给默认值
// 修饰类
final class 类名{
// 代码块
}
// 修饰方法
修饰符 final 返回值类型 方法名(参数列表){
// 方法体
}
// 修饰变量
final 变量类型 变量名 = 值;
在Java中, 提供了四种访问权限, 使用不同的访问权限修饰符修饰时, 被修饰的内容会有不同的访问权限
public : 公共的
protected : 受保护的
default(非关键字,而是默认不写) : 默认的
private : 私有的
public | protected | default | private | |
---|---|---|---|---|
同一类中 | √ | √ | √ | √ |
同一包中(子类与无关类) | √ | √ | √ | |
不同包的子类 | √ | √ | ||
不同包的无关类 | √ |
可见 , public具有最大权限, private则是最小权限; 编写代码时, 如果没有特殊的考虑, 建议这样使用权限
成员变量使用 public , 隐藏细节
构造方法使用public, 方便创建对象成员
方法使永public, 方便调用方法
身体和心脏的关系? 汽车和发动机的关系?
定义 : 内部类将一个类A 定义在类B里面, 里面的那个类A就成为内部类, B则称为外部类
成员内部 : 定义在类中, 方法外的类
访问特点 : 内部类可以直接访问外部类的成员, 包括私有成员; 外部类要访问内部类的成员, 必须要建立内部类的对象
创建内部类对象格式 : 外部类名.内部类名 对象名 = new 外部类名().new 内部类名
// 定义格式
public class 外部类名{
class 内部类名{
}
}
// 创建内部类对象格式
public class Person{
private boolean live = true;
public class Heart{
public void jump(){
// 直接访问外部类成员
if(live){
System.out.println("心脏在跳动");
}else{
System.out.println("心脏在跳动");
}
}
public boolean isLive(){
return live;
}
public void setLive(boolean live){
this.live = live;
}
}
// ==================================
public static void main(String[] args){
// 创建外部类对象
Person p = new Person()
// 创建内部类对象
Heart heart = p.new Heart();
//调用内部类方法
heart.jump();
// 外部类调用内部类方法
p.setLive(false);
}
}
如何使用内部类? 有两种方式:
局部内部类
定义在外部类的成员方法的内部, 不能使用任何修饰符 直接 class 类名 {}
局部内部类, 如果希望访问所在方法的局部变量, 那么这个局部变量必须是有效final的
,这与生命周期有关,与内存的分布
匿名内部类
如果接口的实现类(或者是父类的子类), 只需要使用唯一的一次, 那么这种情况下就可以省略该类的定义, 而改为使用匿名内部类
; 它的本质是一个 带具体实现的 父类或者父接口的 匿名的 子类对象
前提 : 必须继承一个父类 或者 实现一个父接口
定义格式 :
接口名称 对象名 = new 接口名称() {
// 覆盖重写所有抽象方法
};
注意事项 :
调用方法
的时候, 只能调用唯一的一次; 如果希望同一个对象, 调用多次方法, 那么必须给起个名字, 这是就不是匿名对象了实现类/子类名称
, 但是匿名对象是省略了对象名称
; 匿名内部类和和匿名对象不是一回事类作为成员变量类型
自定义类的类型可以作为 其他类的成员变量 使用
接口作为成员变量类型
自定义类 , 接口类 , 实现类; 自定义的类对象调用接口的方法,把接口对象作为方法的参数调用
接口作为方法的参数或者返回值
对象的动态写法 : List
java.lang.Object
类是Java语言中的根类, 即所有类的父亲, 它中描述的所有方法子类都可以使用, 在对象实例化的时候, 最终找的父类就是Object; 如果一个类没有特别指定父类, 那么默认则继承自Object类
接下来主要学习其中的两个
public String toString()
: 返回该对象的字符串表示; 内容为 对象类型+@+内存地址值public boolean equals(Object obj)
: 指示其他某个对象是否与此对象相等重写toString()
如果不希望使用toString方法的默认行为, 则可以对它进行覆盖重写
public class Person{
private String name;
private int age;
@Override
public String toString() {
return "Person name:" +name+',' + "age:" +age;
}
}
重写equals()
默认的Object类 比较的是 " == ", 只要不是同一个对象, 则必然返回false; 因此意义不大
mport java.util.Objects;
public class Person {
private String name;
private int age;
@Override
public boolean equals(Object o) {
// 如果对象地址一样,则认为相同
if (this == o)
return true;
// 如果参数为空,或者类型信息不一样,则认为不同
if (o == null || getClass() != o.getClass())
return false;
// 转换为当前类型
Person person = (Person) o;
// 要求基本类型相等,并且将引用类型交给java.util.Objects类的equals静态方法取用结果
return age == person.age && Objects.equals(name, person.name);
}
}
Objects类
在JDK7添加了一个Objects类在java.utils.Objetcs
, 它提供了一些方法来操作对象,它由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或null-tolerant(容忍空指针的),用于计算对象的hashcode、返回对象的字符串表示形式、比较两个对象。
比较两个对象的时候,Object的equals方法容易抛出空指针异常,而Objects类中的equals方法就优化了这个问题, 源码如下 :
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
// Objects.equals(s1,s2)
java.util.Date
类 表示特定的瞬间, 精确到毫秒; 它的构造函数可以将毫秒值转成日期对象
public Date()
分配Date对象并初始化此对象, 以表示分配它的时间(精确到毫秒)public Date(long date)
分配Date对象并初始化此对象, 以表示自从标准基准时间(即1970年1月1日00:00:00)以来的指定毫秒数我们处于东八区, 基准时间为1970年1月1日08:00:00
简单来说使用无参构造, 可以自动设置当前系统时间的毫秒时刻; 指定long类型的构造参数, 可以自定义毫秒时刻
Date类的成员方法和构造方法
构造方法 : new Date时 传入一个long类型, 生成一个日期对象
成员方法 : getTime() 把日期对象转换成long类型的毫秒值
## DateFormat类
java.text.DateFormat
是日期/时间格式化子类的抽象类, 我们通过这个类可以帮助我们完成日期和文本之间的转换, 也就是可以在Date对象与String对象之间进行来回转换
构造方法
由于DateFormat为抽象类, 不能直接使用, 所以需要使用的子类 java.text.SimpleDateFormat
… 这个类需要一个格式指定格式化或解析的规则
public SimpleDateFormat(String pattern)
: 用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat, 参数pattern是一个字符串, 代表日期时间的自定义格式
格式规则 :
标识字母(区分大小写) | 含义 |
---|---|
y | 年 |
M | 月 |
d | 日 |
H | 时 |
m | 分 |
s | 秒 |
// 创建SimpleDateFormat对象
import java.text.DateFormat
import java.text.SimpleDateFormat
public clas DemoSimpleDateFormat{
public static void main(String[] args){
DateFormat datetime = new DemoSimpleDateFormat("yyyy-MM-dd HH:mm:ss")
}
}
常用方法
public String format(Date date)
: 将Date对象格式化为字符串public Date parse(String source)
: 将字符串解析为Date对象// format()
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
/* 把Date对象转换为String */
public class DemoDateFormateMethod{
public static void main(String[] args){
Date date = new date();
// 创建日期格式化对象, 在获取格式化对象时可以指定风格
DateFormat df = new SimpleDateFormat("yyyy年MM月dd日")
String str = df.format(date);
System.out.println(srt); // 2018年1月23日
}
}
// parse
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
把String转换成Date对象
*/
public class Demo04DateFormatMethod {
public static void main(String[] args) throws ParseException {
DateFormat df = new SimpleDateFormat("yyyy年MM月dd日");
String str = "2018年12月11日";
Date date = df.parse(str);
System.out.println(date); // Tue Dec 11 00:00:00 CST 2018
}
}
练习 : 计算一个人已经出生了多少天了!
package cn.itcast.day11;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
/*
1 使用Scanner类, 获取输入的出生日期
2 使用DateFormat的parse, 将字符串的出生日期,解析为Date格式
3 把Date格式的出生日期转换为毫秒值
4 获取当前的日期, 转换为毫秒值
5 (当前时间的毫秒值 - 出生日期的毫秒值) / 1000/60/60/24
*/
public class Demo01CountTime {
public static void main(String[] args) throws ParseException {
System.out.println("请输入你的出生日期 : 格式为yyyy-MM-dd");
// 获取出生日期的指定格式的字符串
String bthStr = new Scanner(System.in).next();
// 指定要转换成什么格式的日期
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
// 出成日期对象
Date bthDate = df.parse(bthStr);
// 今天的日期对象
Date todayDate = new Date();
// 毫秒值
long bthSecond = bthDate.getTime();
long todaySecond = todayDate.getTime();
// 差值
long secone = todaySecond - bthSecond;
if (secone < 0){
System.out.println("我还没出生呢!");
}else{
System.out.println("出生了"+secone/1000/60/60/24+"天");
}
}
}
java.util.Calendar
它是一个 日历 类, 在Date后出现, 替换了许多Date的方法, 该类将所有可能用到的时间信息封装为静态成员变量
, 方便获取, 日历类就是方便获取各个时间属性的
获取方式
Calendar为抽象类 , 由于语言的敏感性 , Calendar在创建对象的时候并非 new 对象 , 而是通过静态方法创建, 返回子类对象,
public static Calendar getInstance()
: 使用默认时区和语言环境获得一个日历
举例 : Calendar cal = Calendar.getInstance();
常用方法
public int get(int field)
: 返回给定日历字段的值
public void set(int field, int value)
: 将给定的日历字段设置为给定值
public abstract void add(int field, int amount)
: 根据日历的规则, 为给定的日历字段添加或减去指定的时间量
public Date getTime()
: 返回一个表示此Calendar时间值(从历元到现在的毫秒偏移量)的Date对象
日历字段表 :
字段值 | 含义 |
---|---|
YEAR | 年 |
MONTH | 月(从0开始, 可以+1使用) |
DAY_OF_MONTH | 月中的天 (几号) |
HOUR | 时(12小时制) |
HOUR_OF_DAY | 时(24小时制) |
MINUTE | 分 |
SECOND | 秒 |
DAY_OF_WEEK | 周中的天(周日为1, 可以-1使用) |
// get/set方法
import java.util.Calendar
public class CalendarUtil{
public static void main(String[] args){
// 创建Calendar对象
Calendar cal = Calendar.getInstance();
// 设置 year 2000
cal.set(Calendar.YEAR,2000);
// 获取年
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH)+1;
int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
System.out.println(year+"年"+month+"月"+dayOfMonth+"日");
// 2000年5月10日
}
}
// add方法
add() 可以对指定日历字段的值就行加减操作, 如果第二个参数为正数则加上偏移量, 如果为负数则减去偏移量
import java.util.Calendar;
public class Demo08CalendarMethod {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
System.out.print(year + "年" + month + "月" + dayOfMonth + "日"); // 2018年1月17日
// 使用add方法
cal.add(Calendar.DAY_OF_MONTH, 2); // 加2天
cal.add(Calendar.YEAR, -3); // 减3年
System.out.print(year + "年" + month + "月" + dayOfMonth + "日"); // 2015年1月18日;
}
}
// getTime方法
Calendar中的getTime方法并不是获取毫秒时刻, 而是拿到对应的Date对象
import java.util.Calendar;
import java.util.Date;
public class Demo09CalendarMethod {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
Date date = cal.getTime();
System.out.println(date); // Tue Jan 16 16:03:09 CST 2018
}
}
小贴士 :
西方的周日为一周的开始
在Calendar类中, 月份从0 开始
java.lang.System
类中提供了大量的静态方法, 可以获取与系统相关的信息或者系统级操作, 在System类的API文档有 :
public static long currentTimeMillis()
: 返回以毫秒为单位的当前时间
public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)
实际上, currentTimeMillis() 就是获取当前系统时间与历元之间的毫秒差值
import java.util.Date;
public class SystemDemo {
public static void main(String[] args) {
//获取当前时间毫秒值
System.out.println(System.currentTimeMillis()); // 1516090531144
}
}
数组的拷贝是系统级的, 性能很高 ; System.arraycopy方法具有五个参数, 含义分别为
参数序号 | 参数名称 | 参数类型 | 参数含义 |
---|---|---|---|
1 | src | Object | 源数组 |
2 | srcPos | int | 源数组索引起始位置 |
3 | dest | Object | 目标数组 |
4 | destPos | int | 目标数组索引起始位置 |
5 | length | int | 复制元素个数 |
练习 : src数组元素[1,2,3,4,5],dest数组元素[6,7,8,9,10]; 将src数组中前3个元素,复制到dest数组的前3个位置上复制元素前
import java.util.Arrays
public class Demo01SystemArrayCopy{
public static void main(String[] args){
int[] src = new int[]{1,2,3,4,5};
int[] src = new int[]{1,2,3,4,5};
System.arraycopy(src,0,dest,0,3)
// src : [ 1,2,3,4,5]
// dest : [1,2,3,9,10]
}
}
由于String类的内容不可变, 所以我们进行字符串拼接的时候, 总是会在内存中新建一个对象
public class StringDemo{
public static void main(String[] args){
String str = "Hello";
str += "world";
System.out.println(str)
}
}
在APi中对String类有这样的描述 : 字符串是常量, 他们的值在创建后不能更改
以上的案例中, 先创建一个"Hello", 引用sre指向它, 虽有又创建一个"World", 改变str的引用指向它, 随后有拼接形成一个新的字符串对象, 继续改变str的引用指向
java.lang.StringBuilder
查阅API文档, StringBuilder 又称为可变字符序列, 它是一个类似于String的字符串缓冲区 , 通过某些方法调用可以改变该序列的长度和内容
StringBuilder是个字符串的缓冲区,它是一个容器, 容器可以装很多字符串, 并且能够对其中的字符串进行各种操作
她的内部拥有一个数组用来存放字符串内容, 进行字符串拼接时, 直接在数组中加入新的内容, StringBuilder会自动维护数组的扩容(默认16字符空间, 超过自动扩充)
构造方法
public StringBuilder()
: 无参构造, 构造一个空的StringBuilder容器
public StringBuilder(String str)
: 构造一个StringBuilder, 并将字符串添加进去
public class StringBuilderDemo {
public static void main(String[] args) {
StringBuilder sb1 = new StringBuilder();
System.out.println(sb1); // (空白)
// 使用带参构造
StringBuilder sb2 = new StringBuilder("itcast");
System.out.println(sb2); // itcast
}
}
常用方法
public StringBuilder append()
: 添加任意类型数据的字符串形式, 并返回当前对象本身
public StringBuilder toString
: 将当前StringBuilder对象转换为String对象
// append方法
append方法具有多种重载形式,可以接收任意类型的参数。任何数据作为参数都会将对应的字符串内容添加到StringBuilder中
public class Demo02StringBuilder {
public static void main(String[] args) {
//创建对象
StringBuilder builder = new StringBuilder();
//public StringBuilder append(任意类型)
StringBuilder builder2 = builder.append("hello");
//对比一下
System.out.println("builder:"+builder);
System.out.println("builder2:"+builder2);
System.out.println(builder == builder2); //true
// 可以添加 任何类型
builder.append("hello");
builder.append("world");
builder.append(true);
builder.append(100);
// 在我们开发中,会遇到调用一个方法后,返回一个对象的情况。然后使用返回的对象继续调用方法。
// 这种时候,我们就可以把代码现在一起,如append方法一样,代码如下
//链式编程
builder.append("hello").append("world").append(true).append(100);
System.out.println("builder:"+builder);
}
}
StringBuilder已经覆盖重写了Object当中的toString方法
// toString方法
通过toString方法,StringBuilder对象将会转换为不可变的String对象
public class Demo16StringBuilder {
public static void main(String[] args) {
// 链式创建
StringBuilder sb = new StringBuilder("Hello").append("World").append("Java");
// 调用方法
String str = sb.toString();
System.out.println(str); // HelloWorldJava
}
}
Java提供了两个类型系统,基本类型与引用类型,使用基本类型在于效率,然而很多情况,会创建对象使用,因为对象可以做更多的功能,如果想要我们的基本类型像对象一样操作,就可以使用基本类型对应的包装类,如下:
基本类型 | 对应的包装类(位于java.lang包中) |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
装箱与拆箱
基本类型与对应的包装类之间, 来回转换的过程称为"装箱"与"拆箱"
装箱 : 从基本类型转换为对应的包装类对象
Integer i = new Integer(4); // 使用构造函数
Integer iii = Integer.valueOf(4); // 使用包装类中的valueOf
// 以上都是创建了一个int值为4的Integer类的对象
拆箱 : 从包装类对象转换为对应的基本类型
int num = i.intValue()
// 将一个int值为4的Integer类的对象转换为一个int类型的变量
自动装箱与自动拆箱**
由于我们经常要做基本类型与包装类之间的转换,从Java 5(JDK 1.5)开始,基本类型与包装类的装箱、拆箱动作可以自动完成
Integer i = 4;//自动装箱。相当于Integer i = Integer.valueOf(4);
i = i + 5;//等号右边:将i对象转成基本数值(自动拆箱) i.intValue() + 5;
//加法运算完成后,再次装箱,把基本数值转成对象。
基本类型 --> 字符串类型
使用 + 拼接
包装类的静态方法toString(参数), 不是Objetc类的toString() 重载
static String toString(int i)返回一个表示指定整数的 String 对象
String类的静态方法valueOf(参数)
字符串类型 --> 基本类型
除了Character之外, 其他的包装类都有parseXxxxx(“数值类型的字符串”)的静态方法
public static byte parseByte(String s)
: 将字符串参数转换为对应的byte基本类型public static byte parseByte(String s)
: 将字符串参数转换为对应的byte基本类型public static byte parseShort(String s)
: 将字符串参数转换为对应的byte基本类型public static byte parseInt(String s)
: 将字符串参数转换为对应的byte基本类型public static byte parseLong(String s)
: 将字符串参数转换为对应的byte基本类型public static byte parseFloat(String s)
: 将字符串参数转换为对应的byte基本类型public static byte parseDouble(String s)
: 将字符串参数转换为对应的byte基本类型public static byte parseBoolean(String s)
: 将字符串参数转换为对应的byte基本类型public class Demo01WrapperParse{
public static void main (String[] args){
int num = Integer.parseInt("100")
}
}
集合和数组既然都是容器, 他们有啥区别呢
集合按照存储结构分为两大类, 分别是单列集合java.util.Collection
和双列集合java.util.Map
java.util.List
和java.util.Set
, 其中, List
的特点是元素有序, 元素可重复; Set
的特点是元素无序, 不可重复; List
接口的主要实现类有java.util.ArrayList
和java.util.LinkedList
, Set
接口的主要实现类有java.util.HashSet
和java.util.TreeSet
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QLcOjpfi-1589289361015)(assets/1589122270152.png)]
所有单列集合的共性方法
public boolean add(E e)
: 把给定的对象添加到当前集合中
public void clear()
: 清空集合中所有的元素
public boolean contains(E e)
: 判断当中集合中是否包含给定的对象
public boolean remove(E e)
: 把给定的对象在当前集合中删除
public boolean isEmpty()
: 判断当中集合中是否为空
public int size()
: 返回集合中元素的个数
public Object toArray()
: 把集合中的元素 ,存储到数组中
在程序开发中, 经常需要遍历集合中的所有元素; JDK专门提供了一个接口java.util.Iterator
; Iterator
接口也是Java集合中的一员, 它与Collection
,Map
接口有所不同, Collecion
接口与Map
接口主要用于存储元素, 而Iterator
主要用于迭代访问(遍历) Collection
中的元素, 因此, Iterator
对象也被称为迭代器
想要遍历Collection集合, 那么就要获取该集合迭代器完成迭代操作
public Iterator iterator()
: 获取集合对应的迭代器, 用来遍历集合中的元素的迭代的概念
Iterator接口的常用方法
public E next()
: 返回迭代的下一个元素public boolean hasNext()
: 如果有元素可以迭代, 则返回true// 代码实现
public class Demo01Iterator{
public static void main(String[] args){
// 创建一个集合对象
Collection<String> coll = new ArrayList<>();
// 往集合中添加元素
coll.add("姚明");
coll.add("科比");
coll.add("麦迪");
// 多态 左接口 右实现类对象
Iterator<String> it = coll.iterator();
System.out.println(it.next());
// 一次取出迭代器的所有元素
while(it.hasNext()){
System.out.println(it.next());
}
}
}
// 迭代器实现原理
Iterator
增强 for循环也称 for each 循环, 是专门用来遍历数组和集合的, 它的内部实现原理其实是个Iterator迭代器 , 所有在遍历过程中, 不能对集合的元素进行增删改查
格式 : for (集合/数组的数据类型 变量名 : 集合名或者数组名) { }
// 遍历数组
public class NBForDemo{
public static void main(String[] args){
int[] arr = {3,5,6,4,87};
// 使用增强for遍历数组
for(int a:arr){ // a是个变量, 代表数组中的每个元素
System.out.println(a);
}
}
}
泛型 : 不确定的数据类型, 未知的数据类型
好处 :
泛型的定义与使用
我们在集合中会大量使用到泛型, 它能够用来灵活地将数据类型应用到不同的类, 方法, 接口当中; 将数据类型作为参数传递
定义格式 : 修饰符 class 类名<代表泛型的变量>{ }
public class ArrayList<E>{
public boolean add(E e){}
public E get(int index){}
}
使用 : 我们在定义类的时候, 不要固定死 参数类型, 还用 E 来指明它是一个泛型, 这样在使用它的时候, 就可以传递任意的参数类型作为参数, 十分方便
定义含有泛型的方法
格式
修饰符 <泛型> 返回值类型 方法名(参数列表(使用泛型)) { }
含有泛型的方法, 在调用方法的时候确定泛型的数据类型
方法传递什么类型的参数, 泛型就是什么数据类型
public class DenericMethod{
public <E> void method01(E e){
System.out.println(e);
}
}
定义含有泛型的接口
接口的参数类型为泛型, 那么实现类的参数就也是泛型; 在调用实现类的方法时, 传递的参数, 就是泛型的 数据类型
泛型通配符
当使用泛型类或者接口时, 传递的数据中, 泛型类型不确定, 可以通过通配符>表示 ; 但是一旦使用泛型的通配符, 就只能使用Object类中的共性方法, 集合中元素自身无法使用
使用 : 不知道使用什么数据类型来接受的时候, 此时可以使用?, ? 表示未知通配符; 此时不能接受数据, 只能往集合中存储数据 ; 这时不能创建对象使用, 只能作为方法的参数使用
import java.util.ArrayList;
import java.util.Iterator;
public class Demo01Generic {
public static void main(String[] args) {
ArrayList<String> list01 = new ArrayList<>();
list01.add("a");
list01.add("b");
ArrayList<Integer> list02 = new ArrayList<>();
list02.add(1);
list02.add(2);
printArray(list01);
printArray(list02);
}
public static void printArray(ArrayList<?> list){
Iterator it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
}
}
类名<代表泛型的变量>{ }
public class ArrayList<E>{
public boolean add(E e){}
public E get(int index){}
}
使用 : 我们在定义类的时候, 不要固定死 参数类型, 还用 E 来指明它是一个泛型, 这样在使用它的时候, 就可以传递任意的参数类型作为参数, 十分方便
定义含有泛型的方法
格式
修饰符 <泛型> 返回值类型 方法名(参数列表(使用泛型)) { }
含有泛型的方法, 在调用方法的时候确定泛型的数据类型
方法传递什么类型的参数, 泛型就是什么数据类型
public class DenericMethod{
public <E> void method01(E e){
System.out.println(e);
}
}
定义含有泛型的接口
接口的参数类型为泛型, 那么实现类的参数就也是泛型; 在调用实现类的方法时, 传递的参数, 就是泛型的 数据类型
泛型通配符
当使用泛型类或者接口时, 传递的数据中, 泛型类型不确定, 可以通过通配符>表示 ; 但是一旦使用泛型的通配符, 就只能使用Object类中的共性方法, 集合中元素自身无法使用
使用 : 不知道使用什么数据类型来接受的时候, 此时可以使用?, ? 表示未知通配符; 此时不能接受数据, 只能往集合中存储数据 ; 这时不能创建对象使用, 只能作为方法的参数使用
import java.util.ArrayList;
import java.util.Iterator;
public class Demo01Generic {
public static void main(String[] args) {
ArrayList<String> list01 = new ArrayList<>();
list01.add("a");
list01.add("b");
ArrayList<Integer> list02 = new ArrayList<>();
list02.add(1);
list02.add(2);
printArray(list01);
printArray(list02);
}
public static void printArray(ArrayList<?> list){
Iterator it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
}
}
受限泛型 的上限于 下限 : 规定传递的泛型只能是某个的本身或者是他的子类与 父类