2020-10-25

一、StringBuffer和StringBuilder类

1、StringBuffer 类

是一个字符串缓冲区的类,线程安全运行效率低,用户存储可变字符串
构造器:

  StringBuffer  sb = new StringBuffer(); // 创建空字符串的容器
  StringBuffer sb = new StringBuffer(String);// 将字符串使用容器存储
  StringBuffer sb = new StringBuufer(int);//声明指定容量的容器

​ 常用方法:
1.1、append():追加字符串
1.2、delete(int start,int end):删除指定位置的字符
1.3、insert(int start ,String):插入到指定位置
1.4、reverse():反转字符
1.5、capacity():获取初始容量
1.6、ensureCapacity(int):设置最低所需容量

2、StringBuilder类

也是字符串缓冲区的类,它是线程不安全,且运行效率高的可变字符串缓冲类

其StringBuilder的方法与StringBuffer几乎一样

3、面试题

1、StringBuffer、StringBuilder和String的区别
a、在运行速度上 : StringBuilder > StringBuffer > String
​ 原因: String是字符串常量,而StringBuilder和StringBuffer是字符串变量,当需要改变字符串内容时,Stirng重新创建变量并赋值, 而StringBuilder和StringBuffer可直接改原有的值,所有效率高。
b、在线程安全上: StringBuffer > StringBuilder > String
​ 原因: StringBuffer是线程安全的,而StringBuilder线程不安全,在StringBuffer上的很多方法增加同步关键字(synchronized),导致在多个线程运行时,保持数据的完整性和一致性,而StringBuilder的方法并没有同步 ,如果在多线程环境下为了确保数据安全,建议使用StringBuffer ,如果在单线程环境下,提高效率使用StringBuilder。

二、对象的克隆

1、为什么需要克隆?

对于基本数据类型,可以将值直接复制给另一个变量,这里两个变量相互独立,而引用数据类型(自定义类) 对于引用类型的赋值并没有产生新的个体,而是将两个变量的类型指向同一个对象。 (本质只有一个对象),如果想要赋值的对象与原始对象独立,则需要进行“对象克隆”

2、如何克隆

我们知道任意一个类都继承自Object类,其中Object类提供一个clone方法 用于克隆对象。
​ 实现步骤:
​ a、 实现 接口 Cloneable
​ b、重写 clone方法(由于该方法是Object的 protectect修饰 不能直接访问)

3、浅克隆和深克隆

3.1、浅克隆
在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。
Java的对象克隆默认是浅克隆,
3.2、深克隆
在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。
简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。
实现深克隆的方式:
​ 1、需要将克隆对象的引用数据类型 也实现克隆

public class Student implements  Cloneable {
    private int id;
    private String sname;
    private int age;
    //收货地址
    private Address address;
    //实现深克隆
      @Override
    protected Object clone() throws CloneNotSupportedException {
//        return super.clone();
         Student stu = (Student)super.clone();
         // 获取学生的address
        Address address =  (Address) stu.getAddress().clone();
        // 将address对象放入 新克隆的stu中
        stu.setAddress(address);
        return stu;

    }
   }
   
   public class Address implements Cloneable{
    //联系人
    private String contectName;
    //联系电话
     private String contectPhone;
        @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
   }

测试:

 public static void main(String[] args) throws CloneNotSupportedException {
         // 创建学生对象
        Student stu1 = new Student(1001,"马创",22);
        Address address = new Address("马创的女朋友","18888888888");
        // 将收货地址 与该学生对象关联
        stu1.setAddress(address);

        //克隆一个对象
        Student stu2 = (Student)stu1.clone();

        System.out.println(stu1.getId()+"---"+stu1.getSname());
        System.out.println(stu2.getId()+"---"+stu2.getSname());
       // 问题:是否会克隆新的address对象 还是说address的内存地址相同
        System.out.println(stu1.getAddress());
        System.out.println(stu2.getAddress());

        // 对于克隆对象的引用数据类型,它默认不会创建引用数据类型的 新对象
        //  这种方式称为“浅克隆”

        // Java也可以实现深克隆
        System.out.println(stu1.getAddress().getContectName());
        System.out.println(stu2.getAddress().getContectName());
    }

结果:

1001—马创
1001—马创
com.j2008.clones.Address@4554617c
com.j2008.clones.Address@74a14482
马创的女朋友
马创的女朋友

三、枚举类(enum)

1、枚举类型的诞生

在JDK5以前,定义常量需要使用public fiinal static… 单独定义,如果有一组常量,需要定义一组final修饰的类型。这样会很繁琐,JDK5以后推出枚举类型。 可以将一组常量定义一个自定义类,使用是通过该类型直接方法。

2、 枚举类型的语法

 public  enum 枚举类型名称{1,值2,值3...
  }
  访问时: 枚举类型名称.1 

用法1:

public enum Color {
    RED(),YELLOW(),BLUE();

}
 public static boolean isRedColor(Color color){
        if(color.equals(Color.RED)){
            return true;
        }
        return false;
    }
    
 public static void main(String[] args) {
        System.out.println(isRedColor(Color.BLUE));
        System.out.println(isRedColor(Color.RED));
}

用法2: 定义枚举类的属性 并赋值

public enum PROVINCES {
    //枚举的值  在初始化枚举值时,通过构造器给它的属性赋值
    HB("湖北",0),BJ("北京",1),HN("湖南",2),FJ("福建",3);

    //枚举的属性
    private String provinceName;
    private int index;
    //枚举的构造器
    private PROVINCES(String provinceName,int index){
        this.provinceName= provinceName;
        this.index = index;
    }

    public String getProvinceName() {
        return provinceName;
    }

    public void setProvinceName(String provinceName) {
        this.provinceName = provinceName;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }
}

//输出某一枚举的值 name和 index
        System.out.println(PROVINCES.BJ.getProvinceName());
        System.out.println(PROVINCES.BJ.getIndex());

        //遍历所有的枚举的值
         PROVINCES [] provs = PROVINCES.values();
        for(PROVINCES pro :  provs){
            System.out.println(pro.getIndex()+"---"+pro.getProvinceName());
        }

四、Math类

java.lang.Math类用于数学计算的工具类 ,它提供都是静态方法 ,不需要构造Math对象
常用方法:
​ Math.random():获取随机数
​ Math.abs() 获取绝对值
​ Math.ceil(): 向上取整
Math.floor() :向下取整
Math.rint():取接近它的整数 ,如果两个同样接近,往偶数靠近
Math.max(int,int):返回两个数的最大值
Math.min(int,int):返回两个数的最小值
Math.round():四舍五入整数
Math .sqrt():对一个数开平方
Math.pow(double,double),对一个数的几次幂

五、大数据类型BigDecimal

1、BigDecimal类

两个double类型的计算可能导致精度不准确,这里使用java.math.*里面提供了BigDecimal类(提供高精度计算的方法)

public static void main(String[] args) {
         double a= 1.200000;
         double b= 1.35433;
         double c = a+b;
        System.out.println(c);
        System.out.println(0.05+0.01);
        System.out.println(1.0-0.42);  // 会出现精度问题  计算不准确

        // 使用BigDecimal ,先将类型转成字符串 (为了避免精度问题)
        BigDecimal num1 = new BigDecimal("0.051");
        BigDecimal num2 = new BigDecimal("0.012");
        // 两个数相加

        BigDecimal sum = num1.add(num2 ) ;
        // 设置保留2位整数  四舍五入
        sum =sum.setScale(2,BigDecimal.ROUND_HALF_UP);
        System.out.println(sum);

        // 减法
       sum =  num1.subtract(num2);
        System.out.println(sum);

       // 乘法
       sum =  num1.multiply(num2);
        System.out.println(sum);

       // 除法
       sum = num1.divide(num2,2,BigDecimal.ROUND_HALF_UP);
        System.out.println(sum);

    }

2、NumberFormat类

java.text.NumberFormat类 :用于将数值格式转成指定格式并输出字符串形式的类 。
DecimalFormat类: 属于NumberFormat的子类。

 // NumberFormat类是对数值类型的格式化类,其中 DecimalFormat是继承NumberFormat
        // 获取数值的货币表现形式
        NumberFormat nf = NumberFormat.getCurrencyInstance();
        String s1 = nf.format(23424.2);
        System.out.println(s1);

        //获取数值的百分比
        nf = NumberFormat.getPercentInstance();
        s1= nf.format(0.654);
        System.out.println(s1);

        //根据指定的格式匹配百分比
        nf = new DecimalFormat("##.##%");
        s1=nf.format(0.654);
        System.out.println(s1);

        // 根据指定的模式匹配千分比
        nf = new DecimalFormat("##.##\u2030");
        s1 = nf.format(0.6543);
        System.out.println(s1);

        // 根据指定的模式 转成科学计数法
        nf = new DecimalFormat("#.###E0");
        s1 = nf.format(198200);
        System.out.println(s1);

        // 根据指定的模式  将字符串转成 指定格式数值型
        String s2 ="25.3%";
        nf = new DecimalFormat("##.##%");
        Number dd = nf.parse(s2);
        double num = (double)dd;
        System.out.println("这个字符串转成double:"+num);

题目: 控制台输入 n个学生 的分数 , 60分以上的是及格,85分以上的是优秀,问学生的及格率是多少,优秀率是多少,使用百分比表示。

Java的util包

一、日期和日历类

1、日期类 Date

在Java中用于表示日期的类 java.util.Date() ,用于获取日期和时间的对象, 不过这个类的一些方法以及过时(被日历类取代)

​ 创建日期类

Date  date = new Date();
Date date = new Date(long) ;
// 创建日期类的对象
        Date date = new Date();
        //获取当前时间 以 标准格式
        System.out.println(date);
        // 获取当前时间的毫秒数
        System.out.println(date.getTime());
        //通过毫秒数构建当前时区的 时间
        Date date2 = new Date(100000);
        System.out.println(date2);

        // 获取 纪元时间  相隔本地时间8时区
        System.out.println(date.toGMTString());
        System.out.println(date.toLocaleString());

        //测试时间的先后
        System.out.println(date.after(date2));// true
        System.out.println(date.before(date2));//false
        System.out.println(date.compareTo(date2));//比较时间大小
        // 时间转成字符串
        System.out.println(date.toString());

2、日历类 Calendar

java.util.Calendar 是表示日历类, 它是一个单例模式 ,通过 getInstance()获取一个日历对象, 并获取日历的任意时间,日期。
常用方法
​ getInstance() : 获取日历对象
​ get() :获取指定的单位的日历数值 (年,月,日 等等)
​ set():设置指定单位的日历数值
​ add() :添加指定单位的日历数值
​ getTimeInMills() :获取日期的毫秒数

 // 获取当前日历对象
        Calendar cal =  Calendar.getInstance();
        // 日历的属性 (fields)
        System.out.println(Calendar.DATE);
        System.out.println(Calendar.YEAR);
        System.out.println(Calendar.MONTH);
        //获取当前日历的年份
        System.out.println(cal.get(Calendar.YEAR));
        // 获取月份 (返回 0-11)
        System.out.println(cal.get(Calendar.MONTH));
        // 获取日(1-31)
        System.out.println(cal.get(Calendar.DATE));
        // 获取日期
        System.out.println("获取当前日历的日期:"+ cal.getTime());
        // 添加日历 (指定的field 和 数量)
        // 将当前日历添加2个月
        cal.add(Calendar.MONTH ,2);
        System.out.println("新的日期:"+cal.getTime());
        cal.add(Calendar.DATE,-3);
        System.out.println("新的日期:"+cal.getTime());

        // 判断日期的前后 after  before
        // 题目 给定两个日期, 计算相差多少天
        Calendar cal1 = Calendar.getInstance();

计算两个日历(日期)的相差天数/月数

 // 重写构建一个时间
        Calendar cal2 =  Calendar.getInstance();
        // 设置指定的时间
        cal2.set(2020,10,20);
        System.out.println("cal2---"+cal2.getTime());
        // 原始方式  通过毫秒数计算
        long  n =  cal1.getTimeInMillis()-cal2.getTimeInMillis();
        System.out.println("n---->"+n);
        long days =  n/(1000*60*60*24);
        System.out.println("days:"+days);

        // java8 的方式
        LocalDate date1 =   LocalDate.of(2020,2,22);
        LocalDate date2 =   LocalDate.of(2020,3,22);
        long days2 = date1.until(date2,ChronoUnit.DAYS);
        System.out.println("两个时间相差的天数:"+ days2);

3、字符串与日期格式的转换

 /**
     * 字符串转日期
     * @param str
     */
    public static Date strToDate(String str) throws ParseException {
        //使用格式化日期类    指定格式 :yyyy-MM-dd HH:mm:ss
        SimpleDateFormat  sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         // 指定格式的字符串 转成 日期
        return sdf.parse(str);

    }
/**
     * 日期转指定格式的字符串
     */
    public static String dateToString(Date date){
        // 使用同样的方式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
        return sdf.format(date);
    }
 // 调用转换方法
        String s = "2020-10-22 10:10:10";
        Date myDate= strToDate(s); // 注意 这里的格式需要匹配yyyy-MM-dd HH:mm:ss
        System.out.println(myDate);

        // 将日期转成字符串
        System.out.println(dateToString(myDate));

二、正则表达式

1、正则表达式定义
​ 正则表达式(Regular Expression)由字母和符号组成的具有特定意义的公式,用于匹配或检索符合条件的字符串。
​ 例如 在网页上输入用户名,要求用户名必须由数字,字母,下划线,或者长度必须8-16位之间 像这样的满足条件的公式,都是编写的 正则表达式进行验证。

^[a-zA-Z0-9_]{8,16}$

解释: ^ :表示以指定的字符开头
$:以指定符合结尾
[ a-zA-Z]: 字符必须是这个a-z或 A-Z之间的一个字符
​ {8,16}:匹配前面的字符8次到16次之间
正则表达式不属于某一门编程语言,可以在很多语言中使用, 例如 Java ,Python 、JS、MySql
Java提供对正则表达式的支持,有如下类
a、java.util.regex.Pattern 正则表达式的编译器类
b、java.util.regex.Matcher 正则表达式的匹配器
c、java.lang.String 中的方法自动兼容 正则语法

1、 元字符

元字符是组成正则表达式的最基本的符号 ,通常代表一定意义

元字符解释
.匹配任意一个字符
\w匹配一个数字,字母,_ 或汉字 \W :对\w取反
\d匹配一个数字 \D:对\d取反
\s匹配一个空白字符 \S:对\s取反
\b匹配以什么字符开头
^以指定的字符串开头 ,用于正则开始的标志位
$以指定的字符串结尾,用于正则结束的标志位
  1. 匹配以abc开头的字符串
   ^abc  或者  \babc

2、匹配8位数的QQ号码

^\d\d\d\d\d\d\d\d$  或  ^\d{8}$

3、匹配以1开头的手机号

^1\d\d\d\d\d\d\d\d\d\d$
String s ="abc";
        System.out.println(s.matches("abc"));
        System.out.println(s.matches("^abc"));
        s="6";
        System.out.println(s.matches("\\d"));
        s="123456780";
        System.out.println(s.matches("\\d{8}"));

区间段

   //[x] :表示匹配一个字符
        //[abc]:表示匹配a或b或c 一个字符
        System.out.println("a".matches("[abc]"));
        System.out.println("b".matches("[abc]"));
        //[^abc]:匹配不是 a 或b 或c
        System.out.println("b".matches("[^abc]"));
        // [0-9]:匹配任意一个数字
        System.out.println("4".matches("[0-9]"));

重复限定符

正则表达式中用于匹配重复次数的符号

重复限定符意义
*匹配前一个字符0次或多次
匹配前一个字符0次或1次
+匹配前一个字符1次或多次
{n}匹配前一个字符n次
{n,}匹配前一个字符至少n次
{n,m}匹配前一个字符n到m次(包含n次,m次)

转义

如果要匹配的字符串中本身就包含小括号,那是不是冲突?应该怎么办?
针对这种情况,正则提供了转义的方式,也就是要把这些元字符、限定符或者关键字转义成普通的字符,做法很简 答,就是在要转义的字符前面加个斜杠,也就是\即可。 如:要匹配以(ab)开头:

或条件

回到我们刚才的手机号匹配,我们都知道:国内号码都来自三大网,它们都有属于自己的号段,比如联通有 130/131/132/155/156/185/186/145/176等号段,假如让我们匹配一个联通的号码,那按照我们目前所学到的正 则,应该无从下手的,因为这里包含了一些并列的条件,也就是“或”,那么在正则中是如何表示“或”的呢? 正则用符号 | 来表示或,也叫做分支条件,当满足正则里的分支条件的任何一种条件时,都会当成是匹配成 功。
那么我们就可以用或条件来处理这个问题

 // 创建匹配格式的编译器
        String phone ="13388880000";
        Pattern pattern = Pattern.compile("1[356789][0-9]{9}");
        //  根据编译器获取匹配器
        Matcher matcher = pattern.matcher(phone);
        System.out.println("是否匹配目标字符串:"+matcher.matches());

        // 或者简写  匹配年龄 (0-100   01-09 | 10-99 |100)
       boolean flag =  Pattern.matches
               ("(0?[0-9])|([1-9][0-9])|(100)","10");

        System.out.println(flag);

        // 匹配一个特殊字符
        // 匹配一个字符串中是否包含 .
        String s2 ="adb";
        System.out.println(s2.matches(".*b.*"));
        // 因为.表示的所有字符  当匹配.时,需要转义
        System.out.println(s2.matches(".*\\..*"));

        // 对于特殊符号 需要转义   \\
        // 匹配邮箱   包含  任意字符任意个数@域名.com
        //    .cn  .com.cn  .gov  .net
        String email="1@softeem.com";
        System.out.println(email.matches("\\w+@\\w+(\\.[a-z]{2,3}){1,2}"));

        // 匹配一级网址 http://    https://d
        String url="http://www.baidu.com";
        System.out.println(url.matches("https?://www\\.\\w+\\.[a-z]{2,3}"));


        // 匹配生日格式   1900-13-01  ~ 2099-12-31   01  -31
        String birthday="1998-13-10"; // yyyy-MM-dd
        String regexp="((19)|(20))[0-9]{2}-((0[0-9])|(1[0-2]))-((0[1-9])|([1-2][0-9])|(3[0-1]))";
        System.out.println(birthday.matches(regexp));

匹配汉字

// 匹配汉字
        String uname ="张";
        System.out.println(uname.matches("[\\u4E00-\\u9FFF]+"));

三、定时器类(Timer类)

1、Timer类
​ java.util.Timer类 是一个任务调度的定时器类,可以安排任务一次执行,或定期重复执行
​ 创建Timer对象

 Timer timer = new Timer();
 Timer timer = new Timer(name);

​ 常用方法
timer.schedule(TimerTask,2000); 2秒后执行一次任务
timer.schedule(TimerTask,2000,1000); 2秒后开始执行任务,每1s执行一次
2、TimerTask类
​ java.util.TimerTask类是由定时器执行的任务类,是一个抽象类。

import java.util.Timer;
import java.util.TimerTask;

/**
 * ClassName: TestTimer
 * Description:
 * date: 2020/10/22 17:10
 *
 * @author wuyafeng
 * @version 1.0   softeem.com
 */
public class TestTimer {
    //统计打印次数
    static int count=0;
    // 创建一个定时器类
    static Timer timer = new Timer("我的定时器");

    public static void main(String[] args) {

        // 开始定时一个任务
        TestTimer obj = new TestTimer();
        // 多少毫秒之和 执行一次任务
      //  timer.schedule(obj.new MyTask(),2000);
        // 2秒之后开始执行,每隔1s执行一次
        timer.schedule(obj.new MyTask(),2000,1000);



    }
    // 定义内部类时,可以直接使用外部类的属性  和它的对象
    class MyTask extends TimerTask{

        @Override
        public void run() {
            System.out.println("你好!!!");
            count++;

            if(count>=10){
                System.out.println("停止");
                //停止
                timer.cancel();
            }
        }
    }
}

一、泛型

1、泛型定义

​ 泛型(generics)是JDK5.0以后的特性,提供了编译期间安全监测机制,它是将数据类型参数化的一种方式。 例如:在对方法进行编写参数列表时,以前我们需要知道方法的参数类型 ,现在使用泛型机制可以将方法的参数类型也作为 “未知的类型” ,在调用该方法时传递该类型。

2、泛型的使用

2.1 泛型类(generic class)

​ 它是一种具有一个或多个类型变量的类,(一个变量可以有多种类型)

​ 语法

  public  class  类<T>{
      // 类里面的数据类型 和 方法返回值,以及方法的参数都可以使用T  
      // <>里面可以是任意大写字母
      
  }
public class People<T> {
    private T name;
    private T sex;

    public T getName() {
        return name;
    }
       public People(T name,T sex){
        this.name= name;
        this.sex = sex;
    }
    public People(){

    }
}
       // 创建没有指定泛型的对象 ,它默认是Object类型
        People  obj= new People();
        obj.setName("李四");
        System.out.println(obj);
        System.out.println(((String)obj.getName()).length());

        // 创建泛型类的对象
        People<String> people = new People<String>("张三","男");
        System.out.println(people);
        System.out.println(people.getName().length());

定义泛型的字母

T : Type: 变量类型

K: Key : 任意键的类型

V: Value : 任意值的类型

E:ELement 用于定义集合的元素类型

2.2 泛型接口(generic interface)

​ 在接口中定义泛型,使接口的方法可以使用该泛型,实现类实现该接口时需要指定接口的类型、

语法:

public interface Genarator<T> {
    public T getValue();

    public void setValue(T s);


}
public class StringGenarator implements  Genarator<String> {
   private String name;

    @Override
    public String getValue() {
        return name;
    }

    @Override
    public void setValue(String s) {
        this.name=s;
    }

public class StudentGenarator implements Genarator<Student> {
    private Student  stu;

    @Override
    public Student getValue() {
        return stu;
    }

    @Override
    public void setValue(Student s) {
        this.stu = s;
    }
}

泛型接口的好处:

​ 让接口的方法的返回值或参数类型 也参数化 (泛型)

2.3 泛型方法

​ a、为什么会使用泛型方法

​ 当一个类中 只有某个方法需要使用泛型,而不是类的全部方法使用泛型,这时可以将泛型定义的范围缩小,通常我们可以定义进行泛型方法。

b、定义泛型方法

​ 语法:

  public  class 普通类{
      
      public <T>  T  getValue(){
          
      }
      
      public <T> void setValue(T t){
          
      }  
      
  }
public class Convert {
    /**
     * 转成字符串的方法
     * @param <T> : 任意类型

     * @return
     */
    public <T> String convertString(T t){
        return  t.toString();
    }

    public <K,V> V converted(K k){
        return (V)k;// 强转的前提 是k -v 有关系
    }

泛型的好处:

​ 1、 可以对类的数据类型 写通用类型,提高代码的复用性 和 可扩展性

2.4 泛型通配符

​ 在定义泛型时除了可使用大写字母表示一种泛型类以外,还可以使用通配符表示泛型类型,如下三种表示方法

<?> :表示一种通用的泛型类,与相似 <? extends T> :表示 泛型类型是T的子类,或者是T <? super T> : 表示泛型类型是T的父类,或者是T 问题: <?> 与 的区别
  T t = new T() // 语法满足
  ? t = new ?()  // 语法不满足

​ 是一种确定的类型 , 可以表示定义泛型类或泛型方法

<?> 是一种不确定的类型, 不能定义泛型类或泛型方法, 通常用于作为方法的形参
public class Dept<T> {
    // 第一个员工
     private T first;
     // 第二个员工
     private T second;
public class Employee {
    private String ename;

    public String getEname() {
        return ename;
    }

public class Manager extends  Employee {

    // 通过经理对象 给经理赋值名称
    public Manager(String ename){
        super(ename);
    }

// 使用不确定的泛型类型 <?>

    /**
     *
     * 这里的部门的泛型可以是任意类型
     */
    public void showInfo(Dept<?> dept){

        System.out.println(dept.getFirst());

    }

    /**
     * @param dept 的泛型可以是Employee  或者继承自Employee
     * @param dept
     */
    public void showInfo2(Dept<? extends Employee> dept){

        System.out.println(dept.getFirst());
        System.out.println(dept.getSecond());

    }

    /**
     *
     * @param dept 的泛型必须是 Manager 或者 Manager的父类
     */
    public void showInfo3(Dept<? super Manager> dept){

        System.out.println(dept.getFirst());
        System.out.println(dept.getSecond());

    }

    public static void main(String[] args) {
         TestDept obj = new TestDept();

         //创建部门对象
        Dept<String> dept = new Dept();
        dept.setFirst("员工1");
        dept.setSecond("员工2");
         obj.showInfo(dept);

         // 在部门中添加 员工对象
        Dept<Employee> dept2 = new Dept();
        dept2.setFirst(new Employee("小强"));
        dept2.setSecond(new Employee("小花"));
        //这里的dept2的泛型是 Employee
        obj.showInfo2(dept2);

        Dept<Manager> dept3 = new Dept();
        dept3.setFirst(new Manager("张经理"));
        dept3.setSecond(new Manager("王经理"));
        //这里的dept3的泛型是  Manager
        obj.showInfo2(dept3);

        //  调用时 参数的泛型必须是 Manager 或Manager的父类
        obj.showInfo3(dept3);
        obj.showInfo3(dept2);
    }

二、集合框架

1、为什么会有集合?

存储多个元素我们以前学过数组类型, 由于数组类型特点是 相同类型且长度固定 ,如果需要存储某一天的新闻数据,用数组不合理 ,无法确定当天数量。 Java中提供可变长度的存储多个元素的数据类型,还可以存储不同数据结构的数据。这样的类型 就是“集合类型”

​ 数组和集合的区别?

​ a、数组的长度固定,集合的长度可自动扩容

​ b、数组的数据类型固定,集合可以存储任意类型 ,集合可以支持泛型

​ c、数组没有方法,而集合提供大量的方法

​ d、Java中提供一个动态数组 集合类型,或其他结合类型

2、集合的分布图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q8XET4HN-1603632012154)(assets/1591347967566.png)]

2.1 集合的顶级接口: Collection

​ Collection属于单列集合的根接口,它扩展的主要子接口包括 java.util.List 和 java.util.Set接口,

List接口特点存储有序 且 可重复的元素, 而Set接口特点存储无序且不可重复的元素,其中List下扩展常用的实现类包括 java.util.ArrayList 和java.util.LinkedList 和Vector , 其中Set接口下扩展的实现类包括 java.util.HashSet 和 java.util.TreeSet .

​ 集合接口的常用方法:

  • public void add(E) : 把给定的元素添加到集合中

  • public void clear():清空集合中的所有元素

  • public boolean remove(E):删除集合中指定的元素,删除成功返回true

  • public boolean contains(E):判断该元素是否存在集合中

  • public boolean isEmpty():判断是否为空集合对象 null会报异常

  • public int size():获取几个元素的大小

  • publict Object toArray() : 将集合元素转成对象数组

    public static void main(String[] args) {
           //通过接口创建实现类 , 可指定存储的泛型
  
          Collection<String> collection = new ArrayList<String>();
          // 集合中指定了元素的类型  String
          collection.add("hello");  // 默认添加到末尾
          collection.add("hi");
          collection.add("哈哈");
          System.out.println("元素大小:"+ collection.size());
          // 删除集合元素  (后面的原始往前 移动)
          collection.remove("hi");
          System.out.println("元素大小:"+collection.size());
          System.out.println("第一个元素:"+((ArrayList<String>) collection).get(0));
          System.out.println("第二个元素:"+((ArrayList<String>) collection).get(1));
  
          // 判断元素是否存在
          System.out.println("是否存在哈哈:"+collection.contains("哈哈"));
          // 转成数组对象
          Object [] objs = collection.toArray();
          //遍历元素
          for(Object obj : objs){
              System.out.println("数组的元素:"+obj);
          }
          //清空元素  clear
          collection.clear();
          // 大小
          System.out.println("清空后元素的大小(对象依然存在,只能内容为空)"
                  +collection.size());
          // 判断对象中是否是空集合
          System.out.println(collection.isEmpty());
  
        
      }

Iterator 集合遍历接口

    // 直接对集合元素遍历   泛型只能是包装类
          Collection<Integer> scores = new ArrayList<>();
          scores.add(90);
          scores.add(88);
          scores.add(92);
          scores.add(91);
          //遍历集合  使用   ,
          // 再遍历集合时 不能一边遍历集合一边删除集合元素,这样会改变它的遍历的模式
          Iterator<Integer> is = scores.iterator();
          //判断是否有下一个元素   ,如果true ,则可以通过next获取值 
           while(is.hasNext()){
               Integer score = is.next();
               System.out.println(score);
           }
  
  • 2.2 ArrayList类

    ​ java.util.ArrayList是一个数组结构的集合,实现动态数组的功能,扩展所有Collection的方法

    数组结构的本质: 线性结构的顺序结构,ArrayList中使用连续的内存空间存储, 访问时通过下标(元素所在的位置)访问

2.2 ArrayList类:

​ java.util.ArrayList是一个数组结构的集合,实现动态数组的功能,扩展所有Collection的方法

数组结构的本质: 线性结构的顺序结构,ArrayList中使用连续的内存空间存储, 访问时通过下标(元素所在的位置)访问

ArrayList的数据结构

分析一个类的时候,数据结构往往是它的灵魂所在,理解底层的数据结构其实就理解了该类的实现思路,具体的实现细节再具体分析。

ArrayList的数据结构是:

img

说明:底层的数据结构就是数组,数组元素类型为Object类型,即可以存放所有类型数据。我们对ArrayList类的实例的所有的操作底层都是基于数组的。

​ 源码分析参考:https://www.cnblogs.com/zhangyinhua/p/7687377.html#_lab2_0_1

public static void main(String[] args) {
         //  通过ArrayList 创建集合对象
        // 还可以存储自定义对象  默认容量是 10
        ArrayList<String> list = new ArrayList<String>();
        // 存储有序集合
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        // 将元素插入到指定位置
        list.add(1,"ddd");
        //直接 遍历元素  get(Index) 通过下标访问元素
        for(int i = 0 ;i<list.size();i++){
            System.out.println("集合元素:"+ list.get(i));
        }
        // 设置集合的最小容量
        list.ensureCapacity(20);



    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值