1. 静态导入
import 语句可以导入一个类或者某个包中的所有类。
import static语句导入一个类中的某个静态方法或所有的静态方法。
语法
import static 包名.类名.静态方法名
或者
import static 包名.类名.*
例如:我们用到Math类中的abs等静态方法,都需要使用Math.来调用,我们就可以使用静态导入Math中的方法,就可以省略Math.了
package com.bjc.demo;
import static java.lang.Math.max;
public class Demo1 {
public static void main(String[] args) {
System.out.println(max(4,10));
}
}
2. 可变参数
2.1 可变参数的特点
1. 只能出现在参数列表的最后
2. ... 位于变量类型和变量名之间,前后有无空格均可
3. 调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数
2.2 语法
在参数列表的最后 参数类型 ... 参数名
例1:
public static Integer add(int ... args){
int sum = 0 ;
for(int a : args){
sum += a;
}
return sum;
}
例2:
public static Integer add(int x,int ... args){
int sum = x ;
for(int a : args){
sum += a;
}
return sum;
}
3. 增强的for循环
语法
for( [修饰符] type 变量名 : 集合变量名 ) { ... }
注意:
1. 迭代变量名必须在()中定义
2. 集合变量可以是数组或者是实现了Iterable接口的结合类。
3. 在type前面可以加上修饰符,例如:final修饰符,加上final可供内部类使用
4. 基础类型的自动拆箱装箱
注意:
1. 基础数据类型int装箱成Integer的时候,如果int类型的数据在一个字节之类,也就是 -128 ~ 127 之间的时候,装箱的时候,数值相等的对象相等。
例如:下面的a与b相等
Integer a = 3;
Integer b = 4;
2. 为什么 -128 ~ 127 之间的数据装箱的时候,数值相等的会是同一个对象了,因为,这之间的数值使用的频率比较大,我们没必要为其每个数值都创建一个对象,由此引出了java中的一个设计模式——享元设计模式
5. 享元设计模式——Flyweight
享元模式的特点是,复用我们内存中已存在的对象,降低系统创建对象实例的性能消耗。
享元模式的经典实现
public class FontBase
{
private List<string> font = new List<string>();
private string fontName;
public FontBase(string name)
{
this.fontName = name;
}
public FontBase AddFont(string font)
{
this.font.Add(font);
return this;
}
public virtual string FontName
{
get
{
return this.fontName;
}
}
}
// 具体的文字类型类:
public class ChineseFont : FontBase
{
public ChineseFont()
: base("ChineseFont")
{
base.AddFont("ChineseFont");
}
}
public class EnglishFont : FontBase
{
public EnglishFont()
: base("EnglishFont")
{
base.AddFont("EnglishFont");
}
}
// 具体的创建工厂类:
public class FontFactory
{
private Dictionary<string, FontBase> fonts = new Dictionary<string, FontBase>();
public FontBase Create(string name)
{
FontBase fontBase = fonts[name];
if (fontBase != null)
return fontBase;
fontBase = (FontBase)Activator.CreateInstance(Type.GetType(name));
return fontBase;
}
}
享元模式的主旨意在通过共享对象节省总体资源,不过共享对象将导致资源访问上的一些问题,包括对象实例的销毁,还包括线程间的共享和线程内的共享是不同的.
我们知道,一般线程退出的时候,会自动释放线程内部的申请的资源,但是对于线程间共享的对象也许不会自动回收,这些内容需要宿主进程来进行回收,当然可能这些我们也可以通过对象池来完成这样的回收机制。 或者说也可以参考操作系统中的队列的情况,通过回调函数来通知进行对象的回收等。我们在对于项目中需要大量实例对象的创建工作的时候,我们就考虑一下是不是需要享元模式的应用了.
6. 枚举
JDK1.5开始引入了枚举的概念,枚举在java中用enum关键词来表示,其每一个属性就是一个对象
public enum weekdsys{
SUM,MON,TRUE,SAT
}
枚举就相当于一个类,可以定义构造函数、成员变量、普通方法和抛方法。
注意:枚举只有一个成员的时候,可以当做一种单例模式的实现方式。
6.1 带构造方法的枚举
1. 枚举类的构造方法必须是私有的
2. 枚举类的构造方法必须位于枚举属性的后面
public enum Weekddays{
SUM,MON,TRUE,SAT;
private Weekddays(){
}
}
6.2 带参数的构造函数
public enum Weekddays{
SUM,MON(1),TRUE,SAT;
private Weekddays(){
System.out.println("不带参数的构造函数");
}
private Weekddays(int day){
System.out.println("带参数的构造函数");
}
}
6.3 带有抽象方法的枚举
我们可以模拟信号灯,让每一个枚举对象都实现一个抽象的方法,去执行各自的逻辑
例如:
public enum TraLam{
RED{
@Override
public TraLam nextLamp() {
return GREEN;
}
},
YELLOW {
@Override
public TraLam nextLamp() {
return RED;
}
},
GREEN {
@Override
public TraLam nextLamp() {
return YELLOW;
}
};
// 1. 定义抽象方法
public abstract TraLam nextLamp();
}
7. 注解
注解是在JDK1.5引入的新特性。
注解相当于一种标记,加了注解就等于是打了某种标记,没加,则等于没有某种标记,以后,java编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记。
注解可以加在包、类、字段、方法,方法的参数以及局部变量上去。
7.1 三种基本注解
1. 取消警告注解:@SuppressWarnings
2. 标记某个类为过时的注解:@Deprecated
3. 重写注解:@Override
7.2 注解的应用结构
1. 定义注解类
2. 应用了注解的类
3. 对应用了注解类的类进行反射操作。
7.3 自定义注解
7.3.1 新建注解类
自定义注解的定义很简单,只需要在接口定义的interface前面加上@符号即可,也可以直接新建一个annotation
public @interface MyAnnotation {
}
7.3.2 将注解应用在某个类上
@MyAnnotation
public class AnnotationTest {
public static void main(String[] args) {
}
}
7.3.3 对应用了注解类的类进行反射操作
1. 检查某个类上是否有某个注解——isAnnotationPresent
AnnotationTest.class.isAnnotationPresent(MyAnnotation.class);
MyAnnotation.class为注解类的字节码
2. 获取类上的注解对象——getAnnotation
MyAnnotation annotation = AnnotationTest.class.getAnnotation(MyAnnotation.class);
参数MyAnnotation.class为注解类的字节码
7.4 生命周期元注解——@Retention
我们将上面得到的注解对象打印出来,发现,控制台没有任何信息,这是因为,我们自定义的注解类上需要加上元注解@Retention
例如:
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
这时候,在运行main方法,控制台可以成功的打印出注解信息了。
这是为什么呢?因为我们在自定义的注解类上加上了元注解Retention,其值为RetentionPolicy.RUNTIME表示,通知JVM,将注解一直保留到运行期。注解的默认生命周期是class。当我们在java源程序上加了一个注解,这个源程序接着要用javac来编译,javac将源文件编译成class文件,编译成class的时候,可能会将一些注解去掉,所以,我们开始控制台打印不出注解的信息,如果加上了@Retention(RetentionPolicy.RUNTIME),就是提示jvm,该注解的生命周期提升到了运行时,即在编译的时候,不要将注解去掉,所以注解会保留到class文件中,然后程序在使用该class的时候,通过类加载器将class文件加载到内存中(注意:class文件的内容不是字节码,只有由类加载器将class文件的内容加载到内存中之后,类加载器会对class文件进行处理,从而在内存中生成二进制内容才是字节码)的时候,注解信息还存在,所以我们可以打印出注解的信息,由此我们知道一个注解的生命周期有三个阶段。
7.5 注解的生命周期
注解的生命周期有三个阶段,分别表示java源程序——class文件——内存中的字节码,这三个生命周期分别对应着RetentionPolicy枚举常量的三个取值。
7.5.1 java源文件——RetentionPolicy.SOURCE
7.5.2 class文件——RetentionPolicy.CLASS
默认生命周期阶段
7.5.3 内存中的字节码——RetentionPolicy.RUNTIME
7.6 java中常用的元注解
7.6.1 生命周期元注解——@Retention
前面介绍过了,这里不再介绍
7.6.2 作用范围元注解——@Target
Target注解用于标记注解类的作用范围的,作用范围通过枚举类ElementType的常量值来表示,Target的默认值为任何元素。
1. 注解作用于方法
@Target(ElementType.METHOD)
2. 注解作用于类
@Target(ElementType.TYPE)
3. 注解作用于属性
@Target(ElementType.FIELD)
4. 注解多个作用于
Target的作用范围支持数组接收,数组用{}表示,用于表示多个作用范围
例如:
@Target({ElementType.METHOD,ElementType.TYPE})
7.7 为注解增加基本属性
7.7.1 介绍
一个注解相当于一个工牌,通俗的讲,如果你的持有工牌,就是某个公司的员工,否则就不是,如果还想区分是哪个部门的员工,这时候,可以在工牌上增加一个部门来区分,加了属性的标记效果为:@MyAnnotation(color="red")
7.7.2 定义基本属性与应用属性
1. 定义基本属性
只需要在注解类中定义一个函数即可。
例如:给自定义注解定义一个color的属性
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface MyAnnotation {
String color();
}
2. 应用属性
只需要在注解处加上 属性名=属性值 即可。
例如:给color属性赋值
@MyAnnotation(color="yellow")
public class AnnotationTest {
public static void main(String[] args) {
if(AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)){
MyAnnotation annotation = AnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.color());
}
}
}
控制台打印出yellow。
3. 注解的特殊属性value
在java注解中,有一个特殊的属性叫做value,当在应用注解的时候,如果只有一个属性值,那么value是可以省略的
4. 给属性指定默认值
我们通过default关键字来指定属性的默认值。
例如:定义color的默认属性值为red
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface MyAnnotation {
String color() default "red";
}
应用:在使用注解的时候,如果属性有默认值,可以不写属性名=属性值了
@MyAnnotation
public class AnnotationTest {
public static void main(String[] args) {
if(AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)){
MyAnnotation annotation = AnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.color());
}
}
}
控制台打印出red。
7.8 为注解增加高级属性
7.8.1 数组类型的属性
定义:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface MyAnnotation {
String[] colors() default {"yellow","red"};
}
使用:
@MyAnnotation(colors={"red","yellow","black","gray"})
public class AnnotationTest {
public static void main(String[] args) {
if(AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)){
MyAnnotation annotation = AnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.colors().length);
}
}
}
注意:如果数组属性只有一个值,那么大括号可以省略
例如:
@MyAnnotation(colors="red")
public class AnnotationTest {
public static void main(String[] args) {
if(AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)){
MyAnnotation annotation = AnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.colors()[0]);
}
}
}
7.8.2 枚举类型的属性
定义枚举类型的属性:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface MyAnnotation {
String[] colors() default {"yellow","red"};
Lamp lamp();
}
应用:
@MyAnnotation(lamp = Lamp.GREEN)
public class AnnotationTest {
public static void main(String[] args) {
if(AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)){
MyAnnotation annotation = AnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.lamp());
}
}
}
7.8.3 注解类型的属性
定义:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface MyAnnotation {
String[] colors() default {"yellow","red"};
Lamp lamp() default Lamp.GREEN;
MyAnnotation2 annotationAttr();
}
应用
@MyAnnotation(annotationAttr=@MyAnnotation2("abc"))
public class AnnotationTest {
public static void main(String[] args) {
if(AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)){
MyAnnotation annotation = AnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.annotationAttr().value());
}
}
}
注意:注解的实例对象可以用@注解类名来表示。
8. 泛型
泛型是提供给java编译器使用的,可以限定集合中的输入类型,让编译器挡住源代码中的非法输入,编译器编译带类型说明的类型的时候,会去除掉泛型。
8.1 泛型的相关术语
以ArrayList<E>和ArrayList<Integer>为例来说明:
1. 整个ArrayList<E>称之为泛型类型
2. E称之为类型变量或类型参数
3. ArrayList<Integer>称之为参数化的类型
4. Integer称之为类型参数的实例或实际类型参数
5. <>读做type of
6. ArrayList称之为原始类型
注意:
1. 参数化类型与原始类型的兼容性:
1.1 参数化类型可以引用一个原始类型的对象,编译报警告;
Collection<String> c = new Vector();
1.2 原始类型可以引用一个参数化类型的对象编译报警告
Collection c = new Vector<String>();
2. 参数化类型不考虑类型参数的继承关系,即两边要保持一致
3. 在创建数组实例时,数组的元素不能使用参数化类型。
8.2 泛型的基本使用
List<String> list = new ArrayList<String>();
8.3 泛型的通配符——?
使用通配符?可以引用任意类型的参数化类型,?通配符定义的变量主要作为引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。
例如:定义一个方法,用于接收任意泛型类型的集合
public static void printCollection(Collection<?> col){
// System.out.println(col.toString());
for(Object obj : col){
System.out.println(obj);
}
}
注意:
1. 这里?不能用Object来代替。因为 参数化类型不考虑类型参数的继承关系。
2. 这里的形参col不能调用与参数有关的方法
例如:col不能调用add方法添加元素。
因为不管add添加的是一个具体的类型,编译器无法预知调用者传递的具体类型,而在泛型中不存在类型参数的父子关系,参数可以作为引用变量去引用一个变量,但是不能用它去调用一个与类型参数有关的方法。
3. 可以调用与参数无关的方法,例如:上面的col.toString().
8.4 通配符?的扩展——限定通配符
限定通配符总是包括自己
8.4.1 限定通配符的上边界——extends
8.4.2 限定通配符的下边界——super
8.5 自定义泛型方法
泛型的实际类型只能是引用类型,不能是基本数据类型。
例如:定义一个函数,用于实现任意类型的数组的元素交换
public static <T> void swap(T[] arr,int i,int j){
T temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
函数调用
1. 数组为String[]类型
swap(new String[]{"a","c","d","s"},1,3);
2. 数组为int[]类型会编译不通过
swap(new int[]{1,2,3},1,3); // 编译不通过
8.6 自定义泛型类
类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的。
将参数类型定义在类上,这样,在方法上就不用定义类型参数了
package com.bjc.annotation;
public class General<T> {
T field;
public void add(T t){
}
public T findById(Integer id){
return field;
}
public void del(T t){
}
}