写在前面:又是周四的晚上,不知道前四天怎么过的,突然觉得时间原来可以过的这样快。明天又是约定好的日子,不知道前面的路是什么样子,真是命运多舛。今天不知道该怎么分类写了,就把一些java新特性和进阶的散碎知识点复习下吧。
1.静态导入
静态导入语法用于导入类的某个静态属性或方法。使用静态导入可以简化程序对类静态属性和方法的调用。
语法:import static 包名.类名.静态属性|静态方法|*
例如:
import static java.lang.System.out
import static java.lang.Math.*
public class Demo {
public static void main(String[] args) {
// 普通写法
System.out.println("hello world");
int max = Math.max(100, 200);
System.out.println(max);
// 静态导入
out.println("hello world");
int max2 = max(100, 200);
System.out.println(max2);
}
}
2.增强for循环
增强for循环只能用在数组、或实现Iterable接口的集合类上
语法格式:for(变量类型 变量:需迭代的数组或集合){}
For each是为了让你的代码变得简捷、和容易维护。
增强for循环要注意的细节:
(1) 迭代器可以对遍历的元素进行操作,使用增强for循环时,不能对集合中的元素进行操作的。
(2) 增加for循环与普通的for循环区别。
(3) map的遍历。
3.可变参数
Java 允许为方法定义长度可变的参数。
语法:数据类型…变量名。
可变长参数是Object[] 数组。(可变参数里存的是对象数组)
例如:
public static int sum2(int... numbers) {
if (numbers == null) {
System.out.println("可变参数的值为null");
return 0;
}
if (numbers.length == 0) {
System.out.println("可变参数的值的长度为0");
return 0;
}
int sum = 0;
for (int num : numbers) {
sum += num;
}
return sum;
}
可变参数的细节
声明:在一个方法中,最多只能有一个可变参数。可变参数只能放在参数列表的最后面。
调用:当使用可变参数时,可以传0或多个参数。当使用可变参数时,也可以传一个数组进去,就表示多个参数。
使用:在方法内部使用时,就是在使用一个数组。当调用时没有传参数时(传了0个),这时在方法内部的参数数组是有值的(不为null),但长度为0。
4.自动装箱拆箱
自动装箱:指开发人员可以把一个基本数据类型直接赋给对应的包装类。
自动拆箱:指开发人员可以把一个包装类对象直接赋给对应的基本数据类型。
区别:包装类是对象,拥有方法和字段。而基本类型不是。另外一个区别是,包装类是引用传递,而基本类型是值传递。
典型应用:
List list = new ArrayList();
list.add(1);
//list.add(new Integer(1));
int i=list.get(0);
//int j = (Integer)list.get(0);
基本数据类型包装类
基本数据类型 | 包装类 |
byte | Byte |
short | Short |
int | Integer |
long | Long |
boolean | Boolean |
float | Float |
double | Double |
char | Character |
基本数据类型包装为对象:装箱 | 对象变基本数据类型:拆箱 |
优点:
为了使得java的基本类型有更多的功能,java为其所有的基本类型提供了包装类来封装常见的功能。如:最大值、数值转换等。
将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据。
常见应用一:
获取最大最小值MAX_VALUE / MIN_VALUE
System.out.println(Integer.MIN_VALUE);// -2147483648
常见应用二:
基本数据类型和字符串之间的转换
例:Integer的parseInt方法,intValue方法
基本数据类型转换成字符串:
(1) 基本数据类型+””
(2) 基本数据类型.toString(基本数据类型值);
例如 Integer.toString(34); //将34变成了”34”
字符串变基本数据类型
基本数据类型 a=基本数据类型包装类.parse基本数据类型(String str);
常见应用三:
进制转换
十进制转成其他进制:
toBinaryString(int i) //以二进制(基数 2)无符号整数形式返回一个整数参数的字符串表示形式。
toHexString(int i) //以十六进制(基数 16)无符号整数形式返回一个整数参数的字符串表示形式。
toOctalString(int i) //以八进制(基数 8)无符号整数形式返回一个整数参数的字符串表示形式。
其他进制转成十进制:
parseInt(String radix);
parseInt(String s, int radix) //使用第二个参数指定的基数,将字符串参数解析为有符号的整数。
示例:
Integer x = new Integer(5);//装箱
int intValue = x.intValue(); //拆箱
// 自动装箱 new Integer(5);
Integer y = 5;
// 对象加整数,x 进行了自动拆箱,变成了int 型 和5进行加法运算后再将和进行装箱赋给x。
y = y + 5; // 是通过Integer.intValue() 方法进行拆箱
5.枚举类
使用enum定义枚举类
在枚举类中定义枚举值(大写)
enum Gender {
MALE, FEMALE;
}
枚举类具有如下特性:
(1) 枚举类也是一种特殊形式的Java类。
(2) 枚举类中声明的每一个枚举值代表枚举类的一个实例对象。
(3) 与java中的普通类一样,在声明枚举类时,也可以声明属性、方法和构造函数。
(4) 枚举类可以声明抽象方法,但是要有具体的枚举值去实现。
(5) 枚举类也可以实现接口(序列化)、或继承抽象类。
(6) 若枚举类只有一个枚举值,则可以当作单态设计模式使用。
6.泛型
格式:集合类<类类型> 变量名 = new 集合类<类类型>();
什么时候使用泛型?
当类中操作的引用数据类型不确定的时候,就可以使用泛型类。
泛型特点
(1) 声明好泛型类型之后,集合中只能存放特定类型元素
(2) 泛型类型必须是引用类型
(3) 使用泛型后取出元素不需要类型转换
泛型方法:public <泛型的声明> 返回值类型 函数名( 泛型 变量名 )
public T getData(T data) {
return data;
}
泛型类:
类上的泛型声明
修饰符 class 类名<泛型>{
}
public class Data<T>{
}
注意:静态方法不可以使用类中定义的泛型
因为类中的泛型需要在对象初始化时指定具体的类型,而静态优先于对象存在。那么类中的静态方法就需要单独进行泛型声明,声明泛型一定要写在static后,返回值类型之前.
泛型接口:
interface Inter<T> {
void print(T t);
}
泛型通配符:
通配符:?
public void show(List<?> list){
}
可以对类型进行限定范围。
?extends E: 接收E类型或者E的子类型。
?super E: 接收E类型或者E的父类型。
7.对象拷贝
对象的浅拷贝
浅复制(浅克隆)被复制对象的所有变量都含有与原来对象相同的值,而所有的对其他对象的引用仍然只指向原来的对象,换言之,浅复制仅仅复制锁考虑的对象,而不复制它所引用的对象。
示例:
public class Student implements Cloneable{
String name;
int age;
Student(String name,int age){
this.name = name;
this.age = age;
}
public Object clone(){
Object o =null;
try{
o=super.clone();//Object中的clone()识别出你要复制的哪一个对象
}
catch(CloneNotSupportedException e){
System.out.println(e.toString());
}
return o;
}
public static void main(String[] args){
Student s1 = new Student("zhang",18);
Student s2 = (Student)s1.clone();
s2.name = "li";
s2.age = 20;
System.out.println("name="+s1.name+","+"age="+s1.age);//修改学生2后不影响学生1的值
}
}
对象的深拷贝
深复制(深克隆)被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量,那些引用其他对象的变量将指向被复制过的新对象,而不再试原有的那些被引用的对象,换言之,深复制把要复制的对象所引用的对象都复制了一遍。
在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里(腌成咸菜),再从流里读出来(把咸菜回鲜),便可以重建对象。
示例:
public Object deepClone()
{
//将对象写到流里
ByteArrayOutoutStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(this);
//从流里读出来
ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi=new ObjectInputStream(bi);
return(oi.readObject());
}
8.反射
类字节码文件是在硬盘上存储的,是一个个的.class文件。我们在new一个对象时,JVM会先把字节码文件的信息读出来放到内存中,第二次用时,就不用在加载了,而是直接使用之前缓存的这个字节码信息。
字节码的信息包括:类名、声明的方法、声明的字段等信息。在Java中“万物皆对象”,这些信息当然也需要封装一个对象,这就是Class类、Method类、Field类。
通过Class类、Method类、Field类等等类可以得到这个类型的一些信息,甚至可以不用new关键字就创建一个实例,可以执行一个对象中的方法,设置或获取字段的值,这就是反射技术。
Java中的反射是一种强大的工具,它能够创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行链接。反射允许在编写与执行时,使程序代码能够接入装载到JVM中的类的内部信息,而不是源代码中选定的类协作的代码。这使得反射称为构建灵活应用的主要工具。java中它允许运行中的java程序对自身进行检查,或者说“自审”,并能够直接操作程序的内部属性。
(1) Java提供了三种方式获取类的字节码
forName()。forName方法用于加载某个类的字节码到内存中,并使用class对象进行封装
类名.class
对象.getClass()
示例:
/**
* 加载类的字节码的3种方式
* @throws Exception
* */
public void test1() throws Exception {
// 方式一
Class clazz1 = Class.forName("cn.itcast.gz.reflect.Person");
// 方式二
Class clazz2 = Person.class;
// 方式三
Person p1 = new Person();
Class clazz3 = p1.getClass();
}
(2) 通过class获取类型的一些信息
1.getName()类的名称(全名,全限定名)
2.getSimpleName()类的的简单名称(不带包名)
3.getModifiers(); 类的的修饰符
4.创建对象:无参数构造创建对象 newInstance()
5.获取指定参数的构造器对象,并可以使用Constructor对象创建一个实例
Constructor<T> getConstructor(Class<?>... parameterTypes)
示例:
Class clazz1 = Class.forName("cn.itcast.gz.reflect.Person");
// 获取类的名称
String name = clazz1.getName();
System.out.println(name); // cn.itcast.gz.reflect.Person
// 获取类的简单名称
System.out.println(clazz1.getSimpleName()); // Person
// 获取类的修饰符
int modifiers = clazz1.getModifiers();
System.out.println(modifiers);
// 构建对象(默认调用无参数构造.)
Object ins = clazz1.newInstance();
Person p = (Person) ins;
System.out.println(p); // cn.itcast.gz.reflect.Person@c17164
// 获取指定参数的构造函数
Constructor<?> con = clazz1.getConstructor(String.class, int.class);
// 使用Constructor创建对象.
Object p1 = con.newInstance("jack", 28);
System.out.println(((Person) p1).getName());
(3) 通过Class类获取类型中的方法的信息
1.获取公共方法包括继承的父类的方法
getMethods()返回一个数组,元素类型是Method
2.获取指定参数的公共方法
getMethod("setName", String.class);
3.获得所有的方法,包括私有
Method[] getDeclaredMethods()
4.获得指定参数的方法,包括私有
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
(4) 通过Class类获取类型中的字段的信息
1.获取公共字段
Field[] getFields()
2.获取指定参数的公共字段
Field getField(String name)
3.获取所有的字段
Field[] getDeclaredFields()
4.获取指定参数的字段,包括私用
Field getDeclaredField(String name)