一、
1、异常处理
关于捕获异常和处理异常,暂时不讲,我们学习下声明异常和抛出异常。 可以通过 throws 关键字在方法上声明该方法要拋出的异常,然后在方法内部通过 throw 拋出异常对象。
Java throws和throw:声明和抛出异常 (biancheng.net)
关于throw抛出异常:throw 语句用来直接拋出一个异常,后接一个可拋出的异常类对象。
import java.util.Scanner;
public class Test05 {
public boolean validateUserName(String username) {
boolean con = false;
if (username.length() > 8) {
// 判断用户名长度是否大于8位
for (int i = 0; i < username.length(); i++) {
char ch = username.charAt(i); // 获取每一位字符
if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
con = true;
} else {
con = false;
throw new IllegalArgumentException("用户名只能由字母和数字组成!");
}
}
} else {
throw new IllegalArgumentException("用户名长度必须大于 8 位!");
}
return con;
}
public static void main(String[] args) {
Test05 te = new Test05();
Scanner input = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = input.next();
try {
boolean con = te.validateUserName(username);
if (con) {
System.out.println("用户名输入正确!");
}
} catch (IllegalArgumentException e) {
System.out.println(e);
}
}
}
2、关键字
Java语言为了解决并发编程中存在的原子性、可见性和有序性问题,提供了一系列和并发处理相关的关键字,如:synchronized(这里其他关键字暂时不介绍)。
在介绍之前,先阅读下面两个文章:
JVM内存结构 VS Java内存模型 VS Java对象模型-HollisChuang's Blog
- JVM内存结构,由Java虚拟机规范定义。描述的是Java程序执行过程中,由JVM管理的不同数据区域。各个区域有其特定的功能。
- Java的多线程之间是通过共享内存进行通信的,而由于采用共享内存进行通信,在通信过程中会存在一系列如可见性、原子性、顺序性等问题,而JMM(java内存模型)就是围绕着多线程通信以及与其相关的一系列特性而建立的模型。
- JVM内存结构,和Java虚拟机的运行时区域有关。 Java内存模型,和Java的并发编程有关。 Java对象模型,和Java对象在虚拟机中的表现形式有关。
再有人问你Java内存模型是什么,就把这篇文章发给他。-HollisChuang's Blog
- 原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行。
- 可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
- 有序性即程序执行的顺序按照代码的先后顺序执行。
缓存一致性问题其实就是可见性问题。而处理器优化是可以导致原子性问题的。指令重排即会导致有序性问题。
内存模型解决并发问题主要采用两种方式:限制处理器优化和使用内存屏障。
在开发多线程的代码的时候,我们可以直接使用synchronized
等关键字来控制并发,从来就不需要关心底层的编译器优化、缓存一致性等问题。所以,Java内存模型,除了定义了一套规范,还提供了一系列原语,封装了底层实现后,供开发者直接使用。
- 在Java中可以使用
synchronized
来保证方法和代码块内的操作是原子性的。 - Java中的
volatile
关键字提供了一个功能,那就是被其修饰的变量在被修改后可以立即同步到主内存,被其修饰的变量在每次是用之前都从主内存刷新。 - 在Java中,可以使用
synchronized
和volatile
来保证多线程之间操作的有序性。
关于synchronized的用法:
/**
* @author Hollis 18/08/04.
*/
public class SynchronizedDemo {
//同步方法
public synchronized void doSth(){
System.out.println("Hello World");
}
//同步代码块
public void doSth1(){
synchronized (SynchronizedDemo.class){
System.out.println("Hello World");
}
}
}
二、java集合、泛型和枚举
2.1、集合
为了保存数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组),java 提供了集合类。集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。
Java 集合类型分为 Collection 和 Map,它们是 Java 集合的根接口,这两个接口又包含了一些子接口或实现类。
简单样例:
public static void main(String[] args) {
ArrayList list1 = new ArrayList(); // 创建集合 list1
ArrayList list2 = new ArrayList(); // 创建集合 list2
list1.add("one"); // 向 list1 添加一个元素
list1.add("two"); // 向 list1 添加一个元素
list2.addAll(list1); // 将 list1 的所有元素添加到 list2
list2.add("three"); // 向 list2 添加一个元素
System.out.println("list2 集合中的元素如下:");
Iterator it1 = list2.iterator();
while (it1.hasNext()) {
System.out.print(it1.next() + "、");
}
}
ArrayList和LinkedList的区别:
- ArrayList 是基于动态数组数据结构的实现,访问元素速度优于 LinkedList。LinkedList 是基于链表数据结构的实现,占用的内存空间比较大,但在批量插入或删除数据时优于 ArrayList。
- 对于快速访问对象的需求,使用 ArrayList 实现执行效率上会比较好。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高。
Set集合里面有两个常用类:HashSet和TreeSet
如果向 Set 集合中添加两个相同的元素,则后添加的会覆盖前面添加的元素,即在 Set 集合中不会出现相同的元素。
HashSet:
当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据该 hashCode 值决定该对象在 HashSet 中的存储位置。如果有两个元素通过 equals() 方法比较返回的结果为 true,但它们的 hashCode 不相等,HashSet 将会把它们存储在不同的位置,依然可以添加成功。
TreeSet:
TreeSet 类实现的 Set 接口默认情况下是自然排序的,这里的自然排序指的是升序排序。
Map集合:
Map 集合最典型的用法就是成对地添加、删除 key-value 对,接下来即可判断该 Map 中是否包含指定 key,也可以通过 Map 提供的 keySet() 方法获取所有 key 组成的集合,进而遍历 Map 中所有的 key-value 对。
Collections类:
它是java提供的一个操作 Set、List 和 Map 等集合的工具类。Collections 类提供了许多操作集合的静态方法,借助这些静态方法可以实现集合元素的排序、查找替换和复制等操作。
简单样例1:
public class Test1 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
List prices = new ArrayList();
for (int i = 0; i < 5; i++) {
System.out.println("请输入第 " + (i + 1) + " 个商品的价格:");
int p = input.nextInt();
prices.add(Integer.valueOf(p)); // 将录入的价格保存到List集合中
}
Collections.sort(prices); // 调用sort()方法对集合进行排序
System.out.println("价格从低到高的排列为:");
for (int i = 0; i < prices.size(); i++) {
System.out.print(prices.get(i) + "\t");
}
}
}
其他的查找替换操作这里就不放更多例子了。
Iterator:
使用Iterator来遍历Collection集合元素。Iterator(迭代器)是一个接口,它的作用就是遍历容器的所有元素。
样例代码:
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class IteratorTest {
public static void main(String[] args) {
// 创建一个集合
Collection objs = new HashSet();
objs.add("C语言中文网Java教程");
objs.add("C语言中文网C语言教程");
objs.add("C语言中文网C++教程");
// 调用forEach()方法遍历集合
// 获取books集合对应的迭代器
Iterator it = objs.iterator();
while (it.hasNext()) {
// it.next()方法返回的数据类型是Object类型,因此需要强制类型转换
String obj = (String) it.next();
System.out.println(obj);
if (obj.equals("C语言中文网C语言教程")) {
// 从集合中删除上一次next()方法返回的元素
it.remove();
}
// 对book变量赋值,不会改变集合元素本身
obj = "C语言中文网Python语言教程";
}
System.out.println(objs);
}
}
该接口中定义了下面的4个方法:
- boolean hasNext():如果被迭代的集合元素还没有被遍历完,则返回 true。
- Object next():返回集合里的下一个元素。
- void remove():删除集合里上一次 next 方法返回的元素。
- void forEachRemaining(Consumer action):这是 Java 8 为 Iterator 新增的默认方法,该方法可使用 Lambda 表达式来遍历集合元素。
详细内容可见教程,链接:
Java Iterator(迭代器)遍历Collection集合元素 (biancheng.net)
当使用 Iterator 迭代访问 Collection 集合元素时,Collection 集合里的元素不能被改变,只有通过 Iterator 的 remove() 方法删除上一次 next() 方法返回的集合元素才可以,否则将会引发“java.util.ConcurrentModificationException”异常。
关于Collection的其他操作,这里就不多介绍了。
2.2 泛型
集合有个缺点,就是把一个对象“丢进”集合里之后,集合就会“忘记”这个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了 Object 类型。所以为了解决上述问题,从 Java 1.5 开始提供了泛型。泛型可以在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用率。
泛型本质上是提供类型的“类型参数”,也就是参数化类型。我们可以为类、接口或方法指定一个类型参数,通过这个参数限制操作的数据类型,从而保证类型转换的绝对安全。
泛型集合,泛型类,泛型方法。
- 泛型类一般用于类中的属性类型不确定的情况下。在实例化泛型类时,需要指明泛型类中的类型参数,并赋予泛型类属性相应类型的值。
- 是否拥有泛型方法,与其所在的类是不是泛型没有关系。泛型方法使得该方法能够独立于类而产生变化。一般来说编写 Java 泛型方法,其返回值类型至少有一个参数类型应该是泛型,而且类型应该是一致的,如果只有返回值类型或参数类型之一使用了泛型,那么这个泛型方法的使用就被限制了。
2.3 枚举
枚举是一个被命名的整型常数的集合,用于声明一组带标识符的常数。类似这种当一个变量有几种固定可能的取值时,就可以将它定义为枚举类型。
声明枚举时必须使用 enum 关键字。
声明枚举简单样例:
enum Signal {
// 定义一个枚举类型
GREEN,YELLOW,RED
}
public class TrafficLight {
Signal color = Signal.RED;
public void change() {
switch(color) {
case RED:
color = Signal.GREEN;
break;
case YELLOW:
color = Signal.RED;
break;
case GREEN:
color = Signal.YELLOW;
break;
}
}
}
枚举类:
Java 中的每一个枚举都继承自 java.lang.Enum 类。当定义一个枚举类型时,每一个枚举类型成员都可以看作是 Enum 类的实例,这些枚举成员默认都被 final、public, static 修饰,当使用枚举类型成员时,直接使用枚举名称调用成员即可。
enum Signal {
// 定义一个枚举类型
GREEN,YELLOW,RED;
}
public static void main(String[] args) {
for(int i = 0;i < Signal.values().length;i++) {
System.out.println("枚举成员:"+Signal.values()[i]);
}
}
三、java反射机制
Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。