1,泛型,即“参数化类型”。就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定 义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
2,注意
在编译之后程序会采取去泛型化的措施。
- 也就是说Java中的泛型,只在编译阶段有效。
- 在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加 类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段
3,作用
- 1、 提高代码复用率
- 2、 泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)
1,泛型类
public class ClassName
{ private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } }
2,泛型接口
public interface IntercaceName
{ T getData(); } 实现接口时,可以选择指定泛型类型,也可以选择不指定, 如下:
指定类型:public class Interface1 implements IntercaceName
{ private String text; @Override public String getData() { return text; } } 不指定类型:
public class Interface1
implements IntercaceName { private T data; @Override public T getData() { return data; } }
3, 泛型方法
方法的类型是泛型,返回值也是泛型
private static
T 方法名(T a, T b) {} 另一个例子
1,假设在定义时不确定类型,但使用时可以确定:
2,测试方法
在使用泛型时, 可以指定泛型的限定区域 ,
- 例如: 必须是某某类的子类或 某某接口的实现类,格式:
类型通配符是使用?代替方法具体的类型实参。
- 1 extends Parent> 指定了泛型类型的上届
- 2 super Child> 指定了泛型类型的下届
- 3 > 指定了没有限制的泛型类型
上界限定(限定父类)
下界确定
1,Object
2,Objects
1,使用对象的equals方法
2,使用Objects中的equals方法
3,源码
判断对象是否为空,若为空则抛出异常 。常用于要求对象不能为空的场景:
1,相关API
2,应用
1,相关API
2,应用
1,相关API
2,应用
1,相关部分API
2,应用
3,源码
1,输出两个小数之和
通过在控制台运行0.1+0.2。 会发现float和double的运算误差
由于float类型和doub1 e类型在运算时可能会有误差,为了实现精确运算则需要借助java.math. BigDecima 类加以描述
1,需要通过BigDecimal创建对象,相要参与运算的小数的字符串形式作为参数。
2,常用方法
3,转换为特定类型
在JDK 1.1之前, Date类还有两个附加功能。 它允许将日期解释为年,月,日,小时,分钟和秒值。 它还允许格式化和解析日期字符串。 不幸的是,这些功能的API不适合国际化。 从JDK 1.1开始, Calendar类应该用于在日期和时间字段之间进行转换,而DateFormat类应该用于格式化和解析日期字符串。 不推荐使用Date中的相应方法。
目前常用的方法:
1,当前时间
2,指定时间
3,获取时间戳
1,打印当前时间
2,打印提前一小时的时间
用于格式化和解析字符串。DateFormat是一个抽象类,经常使用他的直接子类SimpleDateFormat
1, 格式定义。将时间转换为指定格式
2,使用方法format转换为想要的格式
3,使用方法parse 将指定格式的时间字符串转换为date对象
4,计算时间差
可以解决国际化的问题。Calendar为抽象类,需要通过getInstance获得对象;
1,创建calendar对象,进入Calendar类,找到getInstance方法
2,再次进入createCalendar方法
3,可以看到标准化操作,不同地区的创建方法
1,找到Calendar类中的fields数组,存放年月日时分秒相关的信息
2,这些常量表示相应字段在数组中的下标
3,实际使用运行效果
4,调用过程:get-》internalGet
1,使用get方法,并传入相应参数
2,运行效果
1,使用set方法
1,增加年份add
2,增加月份
注意国外月份0-11表示1-12.此外国外的星期日表示一周的开始
String类表示字符串。 Java程序中的所有字符串文字(例如"abc" )都实现为此类的实例。
- 字符串是不变的; 它们的值在创建后无法更改。
- 字符串缓冲区支持可变字符串。
- 因为String对象是不可变的,所以可以共享它们。(如果两字符串内容完全相同,则采用同一块内存地址)
之前的操作中,可以发现,使用String可以表示出一个字符串,但是使用此类的时候也会发现有一点的问题。
从String命名风格可以发现,String 是一一个类, 但是此类在使用的时候却发现可以不用构造方法而直接赋值,那么这样操作有那些特点呢?
数组的长度无法更改
如果两字符串内容完全相同,则采用同一块内存地址(前提是不通过new实现,只要使用了new就一定会开辟两个不同的内存空间)
1,方法区
方法区(Method Area),又称永久代(Permanent Generation)(永久存储在内存中,就像静态代码块不会执行两次一样), 又称非堆区( Non-Heap space)方法区,又称永久代(Permanent Generation) ,常称为PermGen,位于非堆空间,又称非堆区(Non-Heap space)。
方法区是被所有线程共享。
- 所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。
- 简单说,所有定义的方法的信息都保存在该区域,此区属于共享区间。
- 这些区域存储的是:静态变量 +常量+类信息(构造方法/接口定义) +运行时常量池。
但是,实例变量 存在堆内存中,和方法区无关。
以上,只是逻辑上的定义。在HotSpot中,方法区仅仅只是逻辑上的独立,实际上还是包含在Java堆中,也是就说,方式区在物理上属于Java堆区中的一部分,而永久区(Permanent Generation) 就是方法区的实现。
2、堆(heap)
一个JW实例只存在一一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内
存中,保存所有引用类型的真实信息,以方便执行器执行。堆在逻辑上分为三部分(Perm) :
- 新生代(Young Generation, 常称为YoungGen)
- 老年代(01d Generation, 常称为oldGen、TenuringGen)
- 永久代(Permanent Generation, 常称为PermGen)
2.1、新生区(New/Young Generation)
新创建的对象都在新生代,在15次GC回收中被回收,就是新生代
- 新生代(Young Generation) ,常称为YoungGen,位于堆空间;
- 新生区又分为Eden区和Survior (幸存区)。
Eden :新创建的对象
Survior 0、1:经过垃圾回收,但是垃圾回收次数小于15次的对象
2.2、养老代(01d Generation)
- 老年代(old Generation) ,常称为oldGen,位于堆空间;
- old :垃圾回收次数超过15次,依然存活的对象
2.3、永久区(Permanent Generat ion)
字符串都存在于永久代,所以两个内容相同的字符串引用会指向同一地址(除了new出来的String,因为new一定会开辟新的内存空间)
永久代(Permanent Generation) , 常称为PermGen, 位于非堆空间。
永久区是一个常驻内存区域,用于存放JDK自身所携带的Class , Interface的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭JVM才会释放此区域所占用的内存。
2.3.1、方法区的实现的演变:
- Jdk1.7之前: hotspot虚拟机对方法区的实现为永久代;
- Jdk1. 8及之后: hotspot移除了永久代用元空间(Metaspace),
2.3.2、运行时常量池存和字符串常量池的变化
JDK1.7之前:
- 运行时常量池(包含字符串常量池)存放在方法区,此时hotspot虚拟机对方法区的实现为永久代。
JDK1.7 :
- 字符串常量池被从方法区拿到了堆中;
- 运行时常量池剩下的东西还在方法区,也就是hotspot中的永久代。
JDK1.8 :
- hotspot移除了永久代,用元空间(Metaspace)取而代之。这时候,
- 字符串常量池还在堆,
- 运行时常量池还在方法区,只不过方法区的实现从永久代变成元空间(Metaspace)。
只要是被双引号包住的都是字符串对象,如果没有赋予名称的话,属于匿名对象
1,先声明三个字符串对象 text1,text2,text3
2,将三个字符串对象连接后新地址存放在text1中
3,造成的结果
4,实际应用场景中的副作用
爬虫时,把爬取到的字符串全都拼接起来,很快就会发现内存不够用,因为有很多垃圾在里面(拼接一次产生一次垃圾)。所以拼接字符串,能少做尽量少做,尤其是通过+号拼接;
5,如何拼接大量的字符串?
找到String实现的父类接口CharSequence。可以看到其的两个子类:StringBuffer,StringBuilder。
6,拼接方法
首先通过无参方式创建空的StringBuffer/StringBuilder;
其次通过append方式,进行添加,容量不够就根据动态扩容算法进行扩容。重点是不会在缓冲区存储,通过+号连接,因为是在常量缓冲池中存储,所以不会被回收;
7,将结果转换为String
8,虽然麻烦但是节省内存,所以对字符串操作时,推荐使用此方法