类型转换、ASCII码、除法取余、三元表达式
long x = 100;//int->long自动类型转换(隐式) 1.特点:代码不需要进行特殊处理,自动完成。2.规则:数据范围从小到大。
double y = 2.5F;//2.5 float->double自动类型转换(隐式)
int x1 = (int)100L;//long->int强制类型转换(显式) 范围大到小 需要强转,不能自动转
int y1 = (int)3.99;//double->int 抹去小数位变为3
int z1 = (int)6000000000L;//long->int丢失精度 1705032704
int charToInt = 'A'+1;//char->int
int e = (byte)65+(byte)66;//byte->int byte取值-128~127
int e1 = (byte)65+(short)66;//short取值范围-215~214;
//ASCII码 '0'为48 'A'为65 'a'为97
int charToInt2 = 'A'+0;//65 char底层保存的数字
//除法与取余
int r1 = 10/3;//3
int r2 = 10%3;//1
//三元表达式
int a2 = 3>5? (int) 2.5:1;//冒号两侧都需符号精度要求
switch
public static void main(String[] args) {
int num = 2;
//匹配哪一个case就从哪一个位置向下执行,直到遇到了break或者整体结束为止。
switch (num){
case 1:
System.out.println("你好");
case 2:
System.out.println("我好");
//break;
case 3:
System.out.println("大家好");
break;
case 4:
System.out.println("hello");
break;
default:
System.out.println("hello~hello~hello~");
break;
}
}
do-while()、while-do()、for循环
public static void main(String[] args) {
/*
* do-while()和while-do()初始化语句参数出了循环后还可使用,但for循环初始化语句参数只能for循环内容使用
* 若条件判断从来没满足过,do-while()也会执行1次,while-do()和for()不会执行
* */
System.out.println("=====================================");
int i = 1;
do {
System.out.println("hello" + i);//循环体
i++;//迭代语句,步进语句
} while (i <= 10);//条件判断
System.out.println("======================================");
int x = 1;
while (x<=10){
System.out.println("xHello" + x);//循环体
x++;//迭代语句,步进语句
}
System.out.println("======================================");
for (int j = 1; j <=10 ; j++) {//迭代语句和变量定义在for循环参数里,只能当前for循环使用该参数
System.out.println("jHello" + j);//循环体
}
/*
* for循环的break和continue
* */
System.out.println("==========break=====================");
for (int j = 1; j <=10 ; j++) {
if (j==5){
break;//如果j为5,跳出整个for循环,后续迭代都不要了
}
System.out.println("jHello" + j);
}
System.out.println("===========continue===================");
for (int j = 1; j <=10 ; j++) {
if (j==5){
continue;//如果j为5,当次迭代的此处后续代码不执行了,马上开始下一个迭代继续执行for循环体
}
System.out.println("jHello" + j);/*jHello1 jHello2 jHello3 jHello4 jHello6 jHello7 jHello8 jHello9 jHello10*/
}
}
方法和return
/*
* 方法: 1、方法定义于类中,方法中不可定义方法。2、类中方法无先后顺序。3、方法定义后需要(单独调用、打印调用、赋值调用)才能执行。
* return:1、return的数据和方法返回值类型一致。2、void类型的方法可以不写return也可写为return;。
* 方法中可多个return,要保证最终只有一个被执行到
* */
public class JavaTest {
public static int hello1() {
return 10;
}
public static void hello2() {
System.out.println("hello2");
}
public static void hello3() {
int i = 2;
if (i == 2) {
System.out.println("hello2");
return;
} else {
System.out.println("hello222");
}
}
}
方法重载
/*
* 方法重载:方法名相同,参数类型或参数个数不同。对返回值类型没有要求。
* 方法重载参数不一样。可以用来适配多种用户输入的数值类型提供的方法。 println()方法其实就是一个重载
* */
public class JavaTest {
public static void main(String[] args) {
System.out.println(sum(1,2));
System.out.println(sum(1,2,3));
System.out.println(sum("1",2));
System.out.println(sum(1,"2"));
}
public static int sum(int a,int b){
return a+b;
}
public static int sum(int a,int b,int c){
return a+b+c;
}
public static int sum(String a,int b){
return Integer.valueOf(a)+b;
}
public static String sum(int a,String b){
return String.valueOf(a+Integer.valueOf(b));
//注:如果单返回值不同,但参数列表相同,也不是重载
}
}
访问权限修饰符
public(所有位置的类都可访问)、 protected(同包其他类、其他包该类的子类可访问)、
缺省不写(同包其他类可访问)、private(只能本类内部访问)
数组
/*
* 数组:存放多个同一类型数据值的引用类型且运行期间长度不可变的容器
* 动态初始化默认值:整数默认0、浮点型默认0.0、字符类型默认'\u0000'、布尔类型默认false、引用类型默认null;
* 静态初始化也有默认值过程,只不过马上替换成大括号中的内容了。
* 异常有下标0~arraA.length-1的数组索引越界异常ArrayIndexOutOfBoundsException,
* 没有new数组直接如int [] a = null;a[0]的空指针异常NuLLPointerException。
* 数组作为方法的参数或返回值其实传递进去的是地址值。
* */
public class JavaTest {
public static void main(String[] args) {
//静态初始化
int [] arrayA = new int[]{1,2,3};
int [] arrayB = {1,2,3};
System.out.println(arrayA);//地址值
System.out.println(Arrays.toString(arrayB));//[1, 2, 3]
System.out.println(arrayA[0]);
int num2 = arrayB[2];System.out.println(num2);
arrayB[0]=3;
System.out.println(Arrays.toString(arrayB));//[3, 2, 3]
//动态数组
System.out.println("=====================");
int [] arrC = new int[3];
arrC[0] = 3;
System.out.println(arrC[0]);
System.out.println(arrC[1]);
//遍历数组arrayA.fori
for (int i = 0; i < arrayA.length; i++) {
System.out.println(arrayA[i]);
}
//反转遍历数组arrayA.forr
for (int i = arrayA.length - 1; i >= 0; i--) {
System.out.println(arrayA[i]);
}
}
}
对象数组
com.kdy.domain包
public class Person {
public String name;
public Person(String name) {
this.name = name;
}
public Person() {
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
com.kdy.test包
public class JavaTest {
public static void main(String[] args) {
Person[] array = new Person[3];
Person zhangsan = new Person("zhangsan");
Person wangwu = new Person("wangwu");
Person sunwukong = new Person("sunwukong");
array[0]=zhangsan;
array[1]=wangwu;
array[2]=sunwukong;
System.out.println(Arrays.toString(array));
}
}
ArrayList数组
/*
* ArrayList长度可变的集合,<E>泛型为引用类型
* list.add();添加;list.get(0);索引拿数组元素值;list.remove(0);索引删除;list.size();集合长度;
* list.fori遍历集合
* */
public class JavaTest {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("王五");
list.add("王五1");
list.add("王五2");
System.out.println(list);//[王五]
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
Arrays类和Collections类
常用Arrays.toString(数组) 和 Arrays.sort(数组); Arrays.asList("a","b","c")长度不可变的List
Collections.addAll(所有集合通用);
对于list集合有效:Collections.shuffle(poker); Collections.sort()
com.kdy.domain
public class Person implements Comparable<Person> {
private int id;
private String name;
private int age;
public Person() {
}
public Person(int id, String name,int age) {
this.id = id;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Person o) {
//return 0;//认为元素都是相同的
//return this.id - o.id;//按id升序
return this.age - o.age;//按年龄升序,年龄相同,list集合按存入先后顺序再排
//return o.age - this.age;//按年龄降序
}
}
public class Student {
private int id;
private String name;
private int age;
public Student() {
}
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
com.kdy.test
public class JavaTest {
public static void main(String[] args) throws ParseException {
/*
* 以ArrayList为例,学习Collections类
* */
ArrayList<String> ArrayList = new ArrayList<>();
HashSet<String> hashSet = new HashSet<>();
LinkedList<String> linkedList = new LinkedList<>();
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
Collections.addAll(ArrayList,"a","b","c","d","e","f","g");
Collections.addAll(hashSet,"a","b","c","d","e","f","g");
Collections.addAll(linkedList,"a","b","c","d","e","f","g");
Collections.addAll(linkedHashSet,"a","b","c","d","e","f","g");
System.out.println(ArrayList);//[a, b, c, d, e, f, g]
System.out.println(hashSet);//[a, b, c, d, e, f, g]
System.out.println(linkedList);//[a, b, c, d, e, f, g]
System.out.println(linkedHashSet);//[a, b, c, d, e, f, g]
Collections.shuffle(ArrayList);
Collections.shuffle(linkedList);
//Collections.shuffle(hashSet);//set集合无索引,无法打乱
//Collections.shuffle(linkedHashSet);//set集合无索引,无法打乱
System.out.println("shuffle后==========================");
System.out.println(ArrayList);//[b, e, d, a, f, c, g]...
System.out.println(linkedList);//[a, f, b, g, c, e, d]...
Collections.sort(ArrayList);
Collections.sort(linkedList);
//Collections.sort(hashSet);//set集合无索引,不可根据大小排序
//Collections.sort(linkedHashSet);//set集合无索引,不可根据大小排序
System.out.println("sort后===================================");
System.out.println(ArrayList);//[a, b, c, d, e, f, g]
System.out.println(linkedList);//[a, b, c, d, e, f, g]
/*想要使用Collections.sort()方法必须集合元素的类实现comparable<>接口,并重写接口中的方法*/
java.util.ArrayList<Person> personArrayList = new ArrayList<>();
personArrayList.add(new Person(3,"张678w41",23));
personArrayList.add(new Person(2,"张375342",20));
personArrayList.add(new Person(3,"张34576433",26));
personArrayList.add(new Person(1,"张324552",23));
personArrayList.add(new Person(5,"张34567435",25));
Collections.sort(personArrayList);
System.out.println(personArrayList);
/*想要使用Collections.sort()方法也可也可以Collections.list()比较时使用匿名对象comparator,里面重写排序方法*/
java.util.ArrayList<Student> studentList = new ArrayList<>();
studentList.add(new Student(3,"张678w41",23));
studentList.add(new Student(2,"张375342",20));
studentList.add(new Student(3,"张34576433",26));
studentList.add(new Student(1,"张324552",23));
studentList.add(new Student(5,"张34567435",25));
Collections.sort(studentList, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int result = o1.getAge()-o2.getAge();
if (result==0){
result = o1.getName().charAt(0) - o2.getName().charAt(0);
}
return result;
}
});
System.out.println(studentList);
}
}
Collection集合、List集合、Set集合
顶层:Collcetion接口(定义所有单列集合共享方法,没有带索引的方法)
List接口继承了Collection接口:有序【存取元素顺序相同】、允许元素重复、有索引
Set接口继承了Collection接口:不允许元素重复、无索引
Vector集合实现了List接口:先作了解。
ArrayList集合实现了List接口:底层数组可扩容,有下标,随机访问效率高,尾部增删正常,但越往头部增删越慢(因为得移动批量元素到上一个或下一个索引),扩容时新建复制也耗时,需连续内存空间,开销小一些(只需新建或扩容时数组空间用不了可能)。
LinkedList实现类List接口:底层无头节点的双链表,增删快,随机访问越往后越慢(链表得一个个的履),不需要连续内存空间,开销大一些(节点需保存信息还要保存前后节点的地址值)
TreeSet:继承了AbstractSet(实现了Set接口),先作了解。
HashSet:实现了Set接口,无序((对于存放的顺序而言,读取的顺序是无序的),底层哈希表查询非常快,jdk1.8前采用数组加链表,jdk1.8后的哈希表又采用了数组加链表或数组加红黑树(链表长度超过8自动转为红黑树)的形式提高了查询速度—非常快。
LinkedHashSet:实现了Set接口,且继承了HashSet,有序(对于存放的顺序而言,读取的顺序是无序的))。
Collection方法 、Iterator、foreach、list、set
com.kdy.test
public class JavaTest {
public static void main(String[] args) throws ParseException {
/*
* 以Collection接口的子接口List接口的实现类ArrayList为例看Collection接口抽象方法
* add、clear、remove、isEmpty、size、toArray
* */
ArrayList<String> list = new ArrayList<>();
list.add("abc");//添加add()
list.add("def");
list.clear();//清空clear()
list.add("abc");
list.add("def");
boolean flag = list.remove("def");//移除元素remove()
System.out.println(list.isEmpty());//判断集合是否为空isEmpty()
int size = list.size();//集合元素个数size()
Object[] objects = list.toArray();//集合转数组toArray()
/*
* List接口特有方法如下set和get
* */
list.set(0,"替换的元素");//用元素替换某个下标的元素
list.get(0);//通过索引获取元素
/*
* Iterator遍历集合的方式
* Collection接口返回Iterator对象的方法iterator()
* */
Iterator<String> iterator = list.iterator();//将集合元素放入迭代器中
while (iterator.hasNext()){//判断指针后一个有无元素
String str = iterator.next();//后移指针,取出元素。
System.out.println(str);
}
/*
* 增强for
* */
for (String str :list) {
System.out.println(str);
}
/*
* LinkedList为例看Collection接口抽象方法
* */
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("abc");//添加add()
linkedList.add("def");
linkedList.clear();//清空clear()
linkedList.add("abc");
linkedList.add("def");
boolean flag2 = linkedList.remove("def");//移除元素remove()
System.out.println(linkedList.isEmpty());//判断集合是否为空isEmpty()
int size2 = linkedList.size();//集合元素个数size()
Object[] objects2 = linkedList.toArray();//集合转数组toArray()
/*
* LinkedList特有的方法
* */
linkedList.addFirst("helloFirst");//插入到第头个
linkedList.addLast("lastHello");//插入到最后一个
System.out.println(linkedList);//[helloFirst, abc, lastHello]
linkedList.push("hello");// 推入栈顶 头节点
System.out.println(linkedList);//[hello, helloFirst, abc, lastHello]
String pop = linkedList.pop();//从栈顶弹出,弹出头节点移除并返回
System.out.println(pop);//hello
System.out.println(linkedList);//[helloFirst, abc, lastHello]
String removeFirst = linkedList.removeFirst();//返回值为被移除的元素
linkedList.removeLast();//返回值为被移除的元素
/*
* hashCode
* 普通对象继承object重写了hashCode方法 460141958
* String类重写了Object的hashCode方法,不过"通话"和"重地"的hash值重复
* */
Person person = new Person();
System.out.println(person.hashCode());// 460141958
System.out.println("通话".hashCode());//1179395
System.out.println("重地".hashCode());//1179395
System.out.println(System.identityHashCode("通话"));//1163157884
System.out.println(System.identityHashCode("重地"));//1956725890
System.out.println("通话".hashCode()=="重地".hashCode());//true :哈希冲突了
System.out.println("通话".equals("重地"));//false:使用的String的重写的equals方法:详见String类的equals方法
System.out.println("通话"=="重地");//false:地址值不同,字符串常量池中还是不是一个地址
/*
* hashSet集合
* hashSet采用了数组加链表或数组加红黑树的方式,它的算法是先判断当前元素hash值是否集合中存在,不存在就直接放数组,存在就再比较equals,相等就不替换,不相等就该数组该同等hash值的索引作为头节点,接入该节点作为链表中下一个节点
*
* */
HashSet<String> hashSet = new HashSet<>();
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1.equals(s2));//true
hashSet.add(s1);
hashSet.add(s2);
hashSet.add("abc");
//s1和s2和"abc":地址值不同、HashCode相同、toString如果是String不重写toString的话打印的内容也相同,重写的equals也相等
hashSet.add("通话");
hashSet.add("重地");
System.out.println(hashSet);//[通话, 重地, abc]
//创建hashSet存储Person
HashSet<Person> set = new HashSet<>();
set.add(new Person(1,"张三",20));
set.add(new Person(2,"张三",20));
set.add(new Person(1,"张三",20));
//System.out.println(set);//如果Person不重写equals方法 [Person{id=2, name='张三', age=20}, Person{id=1, name='张三', age=20}, Person{id=1, name='张三', age=20}]
System.out.println(set);// 如果Person重写了equals方法和hashCode方法 [Person{id=2, name='张三', age=20}, Person{id=1, name='张三', age=20}]
/*集合中的泛型
* 使用泛型可将运行期间的异常提升到编译期
* 避免了类型转换时可能出现的异常,存放的是什么类型,取出时就是什么类型
* */
ArrayList<Object> objectList = new ArrayList<>();//不使用泛型的话,集合默认object类型,当我们向下转型时,编译不会报错,但运行时可能会报错(如该集合中存储各种类型的数据,到时候用的时候可能向下转型异常)
objectList.add("abcd");
objectList.add(true);
objectList.add(666);
objectList.add(2.56f);
objectList.add(666L);
Object o = objectList.get(0);
//float o1 = (float)objectList.get(1);//可能忘记之前存放集合中是什么类型的数据了,转型写错会报错
}
}}
com.kdy.domain
public class Person {
private int id;
private String name;
private int age;
public Person() {
}
public Person(int id, String name,int age) {
this.id = id;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return id == person.id &&
age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name, age);
}
}
集合的斗地主案例
public class JavaTest {
public static void main(String[] args) throws ParseException {
ArrayList<String> poker = new ArrayList<>();
String [] colors = {"♠","♥","♣","♦"};
String [] numbers = {"2","A","K","Q","J","10","9","8","7","6","5","4","3"};
poker.add("大王");
poker.add("小王");
for (String num:numbers) {
for (String col:colors) {
poker.add(col+num);
}
}
System.out.println(poker);
Collections.shuffle(poker);
ArrayList<String> player01 = new ArrayList<>();
ArrayList<String> player02 = new ArrayList<>();
ArrayList<String> player03 = new ArrayList<>();
ArrayList<String> dipai = new ArrayList<>();
for (int i = 0; i < poker.size(); i++) {
String p = poker.get(i);
if (i>=51){
dipai.add(p);
}else if(i%3==0){
player01.add(p);
}else if(i%3==1){
player02.add(p);
}else if(i%3==2){
player03.add(p);
}
}
System.out.println(player01);
System.out.println(player02);
System.out.println(player03);
System.out.println(dipai);
}
}
Map集合
map接口:双列集合,一个元素包含key和value(一一对应),key和value类型可不同,key不可重复(重复会覆盖value),value可重复。
HashMap:实现了map接口,底层哈希表,查询特别快,无序(存入元素的顺序和取出元素的顺序可不一致)。
LinkedHashMap:继承了HashMap也实现了Map接口,底层哈希表加链表(加链表保证存入的元素顺序和取出元素顺序一致),有序。
com.kdy.domain
public class Person {
private int id;
private String name;
private int age;
public Person(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public Person() {
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return id == person.id &&
age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name, age);
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
com.kdy.test
public class JavaTest {
public static void main(String[] args) throws ParseException {
/*
* Map接口的方法,以HashMap为例
*map.put()、map.remove()、map.get()、map.containsKey()、map.clear();
* */
HashMap<String, String> hashMap = new HashMap<>();
LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
hashMap.put("key1","value1");
linkedHashMap.put("key1","value1");
hashMap.clear();//清空集合
linkedHashMap.clear();
System.out.println(hashMap);
System.out.println(linkedHashMap);
hashMap.put("key1","value1");//添加元素
hashMap.put("key2","value2");
hashMap.put("key3","value3");
hashMap.put("key4","value4");
hashMap.put("key5","value5");
linkedHashMap.put("key1","value1");
linkedHashMap.put("key2","value2");
linkedHashMap.put("key3","value3");
linkedHashMap.put("key4","value4");
linkedHashMap.put("key5","value5");
hashMap.remove("key1");//根据key删除元素
linkedHashMap.remove("key1");
System.out.println(hashMap);
System.out.println(linkedHashMap);
System.out.println(hashMap.get("key2"));//根据key拿value
System.out.println(linkedHashMap.get("key2"));
System.out.println(hashMap.containsKey("key2"));//查该该map是否包含该key
System.out.println(linkedHashMap.containsKey("key2"));
System.out.println(hashMap.containsValue("value2"));//查该该map是否包含该value,下一步可迭代遍历找到对应的key
System.out.println(linkedHashMap.containsValue("value2"));
/*使用keySet加迭代器的方式遍历map集合*/
Set<String> hashMapKeySet = hashMap.keySet();//hashMap无序
Iterator<String> hashMapKeySetIterator = hashMapKeySet.iterator();
while (hashMapKeySetIterator.hasNext()){
String key = hashMapKeySetIterator.next();
String value = hashMap.get(key);
System.out.println(key+"="+value);
}
Set<String> linkedHashMapKeySet = linkedHashMap.keySet();//linkedHashMap有序
Iterator<String> linkedHashMapKeySetIterator = linkedHashMapKeySet.iterator();
while (linkedHashMapKeySetIterator.hasNext()){
String key = linkedHashMapKeySetIterator.next();
String value = linkedHashMap.get(key);
System.out.println(key+"="+value);
}
/*使用keySet加增强for的方式遍历map集合*/
Set<String> hashMapKeySet2 = hashMap.keySet();//hashMap无序
for (String key:hashMapKeySet2 ) {
String value = hashMap.get(key);
System.out.println(key+"="+value);
}
Set<String> linkedHashMapKeySet2 = linkedHashMap.keySet();//linkedHashMap有序
for (String key:linkedHashMapKeySet2 ) {
String value = linkedHashMap.get(key);
System.out.println(key+"="+value);
}
/*使用entrySet加迭代器遍历map*/
Set<Map.Entry<String, String>> hashMapEntrySet = hashMap.entrySet();
Iterator<Map.Entry<String, String>> hashMapEntrySetIterator = hashMapEntrySet.iterator();
while (hashMapEntrySetIterator.hasNext()){
Map.Entry<String, String> entry = hashMapEntrySetIterator.next();
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+":"+value);
}
Set<Map.Entry<String, String>> linkedHashMapEntrySet = linkedHashMap.entrySet();
Iterator<Map.Entry<String, String>> linkedHashMapEntrySetIterator = linkedHashMapEntrySet.iterator();
while (linkedHashMapEntrySetIterator.hasNext()){
Map.Entry<String, String> entry = linkedHashMapEntrySetIterator.next();
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+":"+value);
}
/*使用entrySet加增强for遍历map*/
Set<Map.Entry<String, String>> hashMapEntrySet2 = hashMap.entrySet();
for (Map.Entry<String ,String> entry:hashMapEntrySet2) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+":"+value);
}
Set<Map.Entry<String, String>> linkedHashMapEntrySet2 = linkedHashMap.entrySet();
for (Map.Entry<String ,String> entry:linkedHashMapEntrySet2) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+":"+value);
}
/*
* 自定义的类的对象作为key,存储hashmap中,需要在类中重写hashcode与equals方法来保证map规则key不重复
*/
HashMap<Person, String> personPositionHashMap = new HashMap<>();//无序
personPositionHashMap.put(new Person(1,"zhangsan",20),"英国");
personPositionHashMap.put(new Person(2,"zhangsan",20),"埃及");
personPositionHashMap.put(new Person(3,"zhangsan",20),"阿联酋");
personPositionHashMap.put(new Person(1,"zhangsan",20),"俄罗斯");
System.out.println(personPositionHashMap);//map集合不遍历,直接打印也可,重写了toString
LinkedHashMap<Person, String> personPositionLinkedHashMap = new LinkedHashMap<>();//有序
personPositionLinkedHashMap.put(new Person(1,"zhangsan",20),"英国");
personPositionLinkedHashMap.put(new Person(2,"zhangsan",20),"埃及");
personPositionLinkedHashMap.put(new Person(3,"zhangsan",20),"阿联酋");
personPositionLinkedHashMap.put(new Person(1,"zhangsan",20),"俄罗斯");
System.out.println(personPositionLinkedHashMap);//map集合不遍历,直接打印也可,重写了toString
}
}
Map计算一个字符串字符每次出现的次数
public class JavaTest {
public static void main(String[] args) throws ParseException {
Scanner scanner = new Scanner(System.in);
String str = scanner.next();
HashMap<Character, Integer> map = new HashMap<>();
for (char c: str.toCharArray()) {
if (map.containsKey(c)){
Integer value = map.get(c);
value++;
map.put(c,value);
}else{
map.put(c,1);
}
}
System.out.println(map);
}
}
map集合斗地主有序版
public class JavaTest {
public static void main(String[] args) throws ParseException {
HashMap<Integer, String> poker = new HashMap<>();
ArrayList<Integer> pokerIndex = new ArrayList<>();
ArrayList<String> colors = new ArrayList<>();
Collections.addAll(colors,"♠", "♥", "♣", "♦");
ArrayList<String> numbers = new ArrayList<>();
Collections.addAll(numbers,"2", "A", "K", "Q","J","10","9","8","7","6","5","4","3");
int index = 0 ;
poker.put(index,"大王");
pokerIndex.add(index);
index++;
poker.put(index,"小王");
pokerIndex.add(index);
index++;
for (String number:numbers) {//一定是numbers在外
for (String color: colors) {
poker.put(index,color+number);
pokerIndex.add(index);
index++;
}
}//这样pokerIndex和poker的key可一一对应,且随着index和pokerIndex的增加,poker中value是逐渐逻辑上纸牌大小越来越小的
Collections.shuffle(pokerIndex);
ArrayList<Integer> dipai = new ArrayList<>();
ArrayList<Integer> play01 = new ArrayList<>();
ArrayList<Integer> play02 = new ArrayList<>();
ArrayList<Integer> play03 = new ArrayList<>();
for (int i = 0; i < pokerIndex.size(); i++) {//pokerIndex中元素顺序被打乱了,i是下标0~size
Integer in = pokerIndex.get(i);
if (in>51){
dipai.add(in);
}else if(i%3==0){
play01.add(in);
}else if (i%3==1){
play02.add(in);
}else if (i%3==2){
play03.add(in);
}//由于pokerIndex和poker中的index一一对应,所以打乱pokerIndex后分发给每个玩家乱序的pokerIndex就可从poker中对应乱序的牌值。
}
Collections.sort(play01);
Collections.sort(play02);
Collections.sort(play03);
Collections.sort(dipai);
JavaTest.lookPoker("play01",poker,play01);
JavaTest.lookPoker("play02",poker,play02);
JavaTest.lookPoker("play03",poker,play03);
JavaTest.lookPoker("dipai",poker,dipai);
}
public static void lookPoker(String name,HashMap<Integer,String> poker,ArrayList<Integer> list){
System.out.print(name+"的牌为:");
for (Integer key: list) {
String value = poker.get(key);
System.out.print(value);
}
System.out.println();
}
}
泛型
com.kdy.domian
/*
* 带泛型的类
* */
public class GenericClass<E> {
private E name;
public E getName() {
return name;
}
public void setName(E name) {
this.name = name;
}
/*
* 带泛型成员方法
* */
public <M> void methods1(M m){//方法中要加上<泛型>,入参中才可写泛型类型,传递什么类型的参数就是什么泛型的方法
System.out.println("成员方法带入参泛型,入参为:"+m);
}
/*
* 带泛型静态方法
* */
public static <S> void methods2(S s){//方法中要加上<泛型>,入参中才可写泛型类型,传递什么类型的参数就是什么泛型的方法
System.out.println("静态方法带入参泛型,入参为:"+s);
}
}
public interface GenericInterface<I> {
public abstract void method(I i);
}
/*
* 实现带泛型的接口 实现类确定接口泛型类型了
* */
public class GenericInterfaceImpl implements GenericInterface<String>{
@Override
public void method(String s) {
System.out.println(s);
}
}
/*
* 实现带泛型的接口 实现类泛型跟着接口走
* */
public class GenericInterfaceImpl2<I> implements GenericInterface<I> {
@Override
public void method(I i) {
System.out.println(i);
}
}
com.kdy.test
public class JavaTest {
public static void main(String[] args) throws ParseException {
/*测试带泛型的类*/
GenericClass gc1 = new GenericClass();
gc1.setName("String类型的数据");
Object name = gc1.getName();//不写泛型默认object类型
GenericClass<String> gc2 = new GenericClass<>();//使用泛型
//gc2.setName(1);//设置int的1就会编译报错了
gc2.setName("abcdefg");
System.out.println(gc2.getName());
/*测试带泛型的方法*/
gc1.methods1("123");
gc1.methods1(new GenericClass<>());
gc2.methods1(666);
GenericClass.methods2(new GenericClass<>());
GenericClass.methods2(666);
GenericClass.methods2("abcdefg");
/*测试带泛型的接口和其实现类中确定其泛型的实现类*/
GenericInterfaceImpl gi1 = new GenericInterfaceImpl();
gi1.method("hello666");
/*测试带泛型的接口和其实现类中实现类泛型跟着接口走的实现类*/
GenericInterfaceImpl2<Integer> gi2 = new GenericInterfaceImpl2<>();
GenericInterfaceImpl2<String> gi22 = new GenericInterfaceImpl2<>();
gi2.method(666);
gi22.method("String类型字符串");
}
/*测试泛型通配符?只能作为方法的参数,不能作为创建对象使用*/
public static void printArray(ArrayList<?> list){
//ArrayList<?> list03 = new ArrayList<?>();//不能作为创建的对象使用泛型通配符
Iterator<?> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
/*泛型通配符泛型上限,必须是Number的类型或其子类*/
public static void getElement1(Collection<? extends Number> coll){
System.out.println("hello666");
}
/*泛型通配符泛型下限,必须是Number的类型或其父类*/
public static void getElement2(ArrayList<? super Number> coll){
System.out.println("hello666");
}
可变参数
当你参数的数据类型确定,参数个数不确定,可使用可变参数。一个方法的参数表只能有一个可变参数,放最后。
public class JavaTest {
public static void main(String[] args) throws ParseException {
/*
定义计算(0-n)整数和的方法
已知:计算整数的和,数据类型已经确定int
但是参数的个数不确定,不知道要计算几个整数的和,就可以使用可变参数add();就会创建一个长度为的数组,new int[o]
*/
System.out.println(JavaTest.add(1, 2, 35));
System.out.println(JavaTest.add(50, 100,780,900,450,60,70,90));
}
public static int add(int ...arr){
System.out.println(arr);//底层是一个数组
System.out.println("数组长度:"+arr.length);
int sum = 0;
for (int a :arr) {
sum+=a;
}
return sum;
}
public static void method(String a,Double b,float f,boolean d,int ...arr){
}
//可变参数特殊(终极)写法
public static void method2(Object ...obj){
}
}
基本数据类型的包装类
byte(-128~127)->Byte short(-215~214)->Short int->Integer long->Long float->FLoat
double->Double char->Character boolean->Boolean
取值范围详见一下连接mysql博客中的数据类型部分:mysql的基础使用_阳光明媚UPUP的博客-CSDN博客
Integer a = new Integer(1)//int转Integer;a.intValue()//Integer转int;
jdk1.5后支持自动拆装箱。
Java内存划分
1、栈Stack:方法局部变量(入参或方法体中的参数),方法运行在栈中。超出作用域即消失
2、堆Heap:new出来的东西放堆中,均有16进制地址值可被引用。堆中数据默认值整数默认0、浮点型默认0.0、字符类型默认'\u0000'、布尔类型默认false、引用类型默认null;
3、方法区Method Area:存储.class相关信息含方法的信息。
4、本地方法栈Native Method Stack:与操作系统有关。
5、寄存器pc Register:与CPU相关。
局部变量与成员变量
成员变量在类中,整个类中均可用,存放堆中,有默认值,生命周期随对象创建和回收;
局部变量在方法中只能本方法可用,存放栈中,无默认值,生命周期随方法进栈出栈。
this关键字
访问本类(对象)成员内容
public class Person {
String name ;
public void sayHello(String name){
System.out.println(name+"你好,我是"+this.name);
System.out.println(this);
}
public static void main(String[] args) {
Person person = new Person();
person.name = "张三";
person.sayHello("王五");
}
}
Java的API文档
Java 8 中文版 - 在线API手册 - 码工具
打开在线文档后,ctrl+f查找包,再找到某个类如Date类,看类的解释和说明,学习构造和使用成语方法。
Scanner类
/*
*Scanner类
* */
public class JavaTest {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int intNum = sc.nextInt();//int数字类型
System.out.println(intNum);
String strNum = sc.next();//String类型
System.out.println(strNum);
}
}
Random类
/*
* Random类
* */
public class JavaTest {
public static void main(String[] args) {
Random r = new Random();
int intNum = r.nextInt();
System.out.println(intNum);//1299152027 -1834692549 -1102748919 1094924722 ...
int intNum2 = r.nextInt(10);//范围0-9
System.out.println(intNum2);
int intNum3 = r.nextInt(100)+1;//范围1-10
System.out.println(intNum3);
}
}
Math类
Math.PI圆周率、Math.abs(doubleNum)绝对值、Math.ceil(doubleNum)向上取整、
Math.floor(doubleNum)向下取整、Math.round(doubleNum)四舍五入
String字符串类
/*
* String字符串:内容永不可变,效果相当于char[],底层原理为byte[]
* 字符串常量池:直接写上的双引号字符串,就在字符串常量池中。jdk1.8的字符串的常量池在堆中
* ==和equals
* 字符串常用方法:str.length()字符串长度、str.concat(str1)拼接字符串、str.charAt(3)获取索引字符、str.indexOf("el")查找第一次出现位置(没有返回-1)
* str.subString(3)从下标3截取到尾、str.subString(0,3)从下标0(包含)截取到下标3(不包含)
* str.replace("l","A")替换字符串全部替换
* str.toCharArray()转为字符数组
* str.split(",")分割字符串为字符串数组,英文.要转义str.split("\\.")
* */
public class JavaTest {
public static void main(String[] args) {
String str = "hello";//直接创建 在字符串常量池中
String str2 = "hello";
String str5 = "HELlo";
String str6 = "a,b,c,d,e";
String str7 = "a.b.c.d.e";
String helloStr = new String();//new一个空字符串
char [] charArr = {'h','e','l','l','o'};
String str3 = new String(charArr);//根据char数组创建
byte [] byteArr = {97,65,48};
String str4 = new String(byteArr);//根据byte数组 aA0
System.out.println(str==str2);//true 字符串常量池中的相等
System.out.println(str==str3);//false new出来放到堆中的地址值肯定和string常量池中的元素不相等
System.out.println("============================");
System.out.println(str.equals(str2));//true 字符串常量池中的相等
System.out.println(str2.equals(str3));//true String重写了equals()和hashCode()
System.out.println(str.equalsIgnoreCase(str5));//true String重写了equals()和hashCode()
System.out.println("============================");
System.out.println(str.length());
System.out.println(str.concat("666"));
System.out.println(str.charAt(3));
System.out.println(str.indexOf("el"));
System.out.println("============================");
System.out.println(str.substring(3));
System.out.println(str.substring(0,3));
System.out.println("============================");
System.out.println(str.replace("l","A"));//heAAo
System.out.println("============================");
System.out.println(str.toCharArray()[3]);
System.out.println("============================");
System.out.println(Arrays.toString(str6.split(",")));
System.out.println(Arrays.toString(str7.split("\\.")));
}
}
统计字符串大小写与数字出现的次数
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String input = scanner.next();
int countUpper = 0;
int countLower = 0;
int countNumber = 0;
int countOther = 0;
char[] charArray = input.toCharArray();
for (int i = 0; i < charArray.length; i++) {
char ch = charArray[i];
if ('A'<=ch &&ch <= 'Z'){
countUpper++;
}else if ('a'<=ch &&ch <= 'z'){
countLower++;
}else if ('0'<=ch &&ch <= '9'){
countNumber++;
}else{
countOther++;
}
}
System.out.println(countUpper);
System.out.println(countLower);
System.out.println(countNumber);
System.out.println(countOther);
}
==和equas、重写equals、hashCode、地址值
==对基本数据类型比较的是值,==对引用数据类型比较引用指针地址值。
equals在比较对象没有重新equals方法时等同于==,比较对象需重写equals和hashCode
.equals()如果比较双方一个常量—变量,推荐把常量字符串写在前面。
com.kdy.domain
public class Person {
}
/*
* 该类有属性,且重写了hashCode和equals方法,后期创建的对象只要这两个属性id和name相等,则这些对象的地址值和hash值都是相等的。
* */
public class Person2 {
private int id;
private String name;
public Person2() {
}
public Person2(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;//内存地址值相同返回true
if (o == null || getClass() != o.getClass()) return false;//入参为空或入参和本类不是同一个类返回false
Person2 person2 = (Person2) o;//入参和本类是同一个类,先把入参强转为本类
return id == person2.id &&//当入参的对象的id和本类对象的id(基本类型)相等时 且 参的对象的name(引用类型)和本来的name相等时【要么地址值相等,要么根据String类型的equals判断相等】时 ,返回true
Objects.equals(name, person2.name);//return (a == b) || (a != null && a.equals(b));
}
@Override
public int hashCode() {
return Objects.hash(id, name);//将对象的属性id和name作为参数求hash作为该对象的hash码
}
}
com.kdy.test
public class Test {
public static void main(String[] args) {
/*
* Person中未重写equals和hashCode,equals方法比较栈中地址值
* */
Person person11 = new Person();
Person person12 = new Person();
//toString:打印object父类的toString return getClass().getName() + "@" + Integer.toHexString(hashCode());
System.out.println(person11);//直接打印的其实就是调用的toString()方法 com.kdy.domain.Person@1b6d3586
System.out.println(person12);//直接打印的其实就是调用的toString()方法 com.kdy.domain.Person@4554617c
System.out.println(person11.toString());//com.kdy.domain.Person@1b6d3586
System.out.println(person12.toString());//com.kdy.domain.Person@4554617c
System.out.println(person11.toString().equals(person12.toString()));//两个对象的toString不相等 false 比较的是String类型的重写的equals和hashCode
System.out.println(person11.toString()==person12.toString());//false 比较的是String类型的地址值
//地址值:打印两个对象的实际地址值 System.identityHashCode(obj)
System.out.println(System.identityHashCode(person11));//460141958
System.out.println(System.identityHashCode(person12));//1163157884
System.out.println(person11==person12);//false :==比较地址值 460141958!=1163157884
//hash值:打印两个对象的hash值:发现和地址值相等
System.out.println(person11.hashCode());//460141958
System.out.println(person12.hashCode());//1163157884
System.out.println(person11.hashCode()==person12.hashCode());//false
//因为hash值(等于地址值)不相等导致toString也不相等(未重写):因为object父类的toString return getClass().getName() + "@" + Integer.toHexString(hashCode());
//未重写equals和hashCode 使用equals比较为false(即使两个对象的各个成员属性都一致)
System.out.println(person11.equals(person12));//false
/*
* Person2中有id和name两个属性,且重写了hashCode和equals方法(地址值和hash值根据id和name确定,
* id和name一样hash和地址值就都一样),即使id和name都不给值创建空对象,也是equals相等的。
* */
Person2 person21 = new Person2();
Person2 person22 = new Person2();
//toString:打印object父类的toString return getClass().getName() + "@" + Integer.toHexString(hashCode());
System.out.println(person21);// 直接打印的其实就是调用的toString()方法 com.kdy.domain.Person2@3c1
System.out.println(person22);//直接打印的其实就是调用的toString()方法 com.kdy.domain.Person2@3c1
System.out.println(person21.toString());//com.kdy.domain.Person2@3c1
System.out.println(person22.toString());//com.kdy.domain.Person2@3c1
System.out.println(person21.toString().equals(person22.toString()));//两个对象的toString相等 true 比较的是String类型的重写的equals和hashCode
System.out.println(person21.toString()==person22.toString());//false 比较的是String类型的地址值
//地址值:打印两个对象的实际地址值 System.identityHashCode(obj)
System.out.println(System.identityHashCode(person21));//1956725890
System.out.println(System.identityHashCode(person22));//356573597
System.out.println(person21==person22);//false :==比较地址值 956725890!=356573597
//hash值:打印两个对象的hash值
System.out.println(person21.hashCode());//961
System.out.println(person22.hashCode());//961
System.out.println(person21.hashCode()==person22.hashCode());//true
//因为hash值相等导致toString也相等(未重写):因为object父类的toString return getClass().getName() + "@" + Integer.toHexString(hashCode());
//equals方法:两个对象的类重写了equals方法,比较的是要么地址值相等返回true,要么两个对象的重写equals中的成员属性都相等就返回true
System.out.println(person21.equals(person22));//true
}
}
String类重写的equals和hashCode
public class Test {
public static void main(String[] args) {
/*
* hashCode
* 普通对象继承object重写了hashCode方法 460141958
* String类重写了Object的hashCode方法,不过"通话"和"重地"的hash值重复
* */
Person person = new Person();
System.out.println(person.hashCode());// 460141958
System.out.println("通话".hashCode());//1179395
System.out.println("重地".hashCode());//1179395
/*
* String的重写的hashCode:
public int hashCode() {
int h = hash; //hash是int类型成员变量默认是0
if (h == 0 && value.length > 0) {//为0,且当前String长度大于零
char val[] = value;//val[]接收当前String字符串数组
for (int i = 0; i < value.length; i++) {//进行迭代算出最终的h
h = 31 * h + val[i];
}
hash = h;//返回h
}
return h;
}
* */
System.out.println(System.identityHashCode("通话"));//1163157884
System.out.println(System.identityHashCode("重地"));//1956725890
System.out.println("通话".hashCode()=="重地".hashCode());//true :哈希冲突了
System.out.println("通话".equals("重地"));//false:使用的String的重写的equals方法
/*
* String的重写的equals:
public boolean equals(Object anObject) {
if (this == anObject) { //地址值相同表示同一对象
return true;
}
if (anObject instanceof String) { //如果是String类型多态的再向下转型转成String类型,如果不是String类型地址值又不同直接返回false
String anotherString = (String)anObject;
int n = value.length; //当前String对象的长度
if (n == anotherString.value.length) { //如果入参String对象的长度和当前对象长度不等,返回false,相等继续往下判断
char v1[] = value;
char v2[] = anotherString.value;
int i = 0; //然后就开始当前String和入参String都转成字符数组,一个个的匹对,匹对完也完全一致就返回true,有差错就返回false
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
* */
System.out.println("通话"=="重地");//false:地址值不同,虽然他俩独特哈希冲突,但字符串常量池中还是不是一个地址
}
}
String类重写的hashCode
Static关键字
属于类的,所有对象共用一份
静态成员变量举例:
com.kdy.domain下的
public class Student {
private int id;
private String name;
private Integer age;
public static String room;
private static int idCounter = 0;//使用static实现id自增
public Student(String name, Integer age) {
this.name = name;
this.age = age;
this.id = ++idCounter;//使用static实现id自增
}
public Student() {
this.id = ++idCounter;//使用static实现id自增
}
public int getId() {
return id;
}
}
com.kdy.test下的
public class JavaTest {
public static void main(String[] args) {
Student zhangsan = new Student("zhangsan", 20);
zhangsan.room = "701";
Student wangwu = new Student("wangwu", 20);
System.out.println(wangwu.room);
System.out.println(zhangsan.getId());
System.out.println(wangwu.getId());
}
}
静态方法举例
com.kdy.domain包
public class Student {
private static int num;
private int age;
public void method(){
System.out.println("成员方法");
}
public static void methodStatic(){
methodStatic2();
num=2;
//method();//报错
//age=2;//age报错
System.out.println("静态方法");
}
public static void methodStatic2(){
}
}
com.kdy.test包
public class JavaTest {
public static void main(String[] args) {
Student student = new Student();
student.method();
Student.methodStatic();
Student.num=2;//静态方法中只能直接访问静态方法或变量,不能直接访问其他成员变量或成员方法
methodOwnStatic();
new JavaTest().methodOwn();
}
private static void methodOwnStatic(){
System.out.println("自己的静态方法");
}
private void methodOwn(){
System.out.println("自己的成员方法");
}
}
静态方法中不能直接访问非静态的内容(成员方法成员变量),只能直接访问静态方法或变量
静态方法中不能使用this或super等表示对象的关键字。
存放位置:静态变量和类.class一起存放在方法区中。
静态代码块
第一次用到本类时,执行唯一的一次。
典型用途:一次性的为静态成员变量赋值。
package com.kdy.domain;
public class Person {
static {
System.out.println("本类的静态代码块执行...");
}
public Person() {
System.out.println("无参构造执行...");
}
}
package com.kdy.test;
public class JavaTest {
public static void main(String[] args) {
Person person = new Person();
Person person2 = new Person();
}
}
结果:
本类的静态代码块执行...
无参构造执行...
无参构造执行...
继承
父类(基类)、子类(派生类)
继承解决的是共性抽取问题。子类既可有父类内容,也可有自己专属内容。
继承是多态的前提。
一个子类只能继承一个父类,但可多级继承A->B->C。一个父类可被多个子类继承。
com.kdy.domain中Fu
public class Fu {
public int num = 10;
public int fuNum = 10;
public void sameMethod(){
System.out.println("父类重名方法");
}
public void fueMethod(){
System.out.println("父类自己的方法");
}
public Fu() {
System.out.println("父类构造执行");
}
public Fu(int num, int fuNum) {
System.out.println("父类有参构造");
this.num = num;
this.fuNum = fuNum;
}
public Fu(int num) {
System.out.println("父类有参构造");
this.num = num;
}
public static void staticMethod(){
System.out.println("父类静态方法");
}
protected Fu overrideMethod(){//子类可重写父类的成员方法
return new Fu();
}
}
com.kdy.domain中Zi
public class Zi extends Fu{
public int num= 20;
public int age= 20;
public void sameMethod(){
System.out.println("子类重名方法");
}
public void ziMethod(){
int num = 30;
System.out.println(num);
System.out.println(this.num);
this.sameMethod();//this关键字:访问本类成员变量、成员方法中访问另一个成员方法、构造方法中访问另一个构造方法
System.out.println(super.num);
super.fueMethod();//super关键字:调用父类成员变量、成员方法、构造方法
}
public Zi() {
//super();//不写也是子类构造中先加载父类构造
super(65);//如果子类构造中要写super,只能写一个。
System.out.println("子类构造执行");
}
public Zi(int num) {
this(32,23);
this.num = num;
}
public Zi(int num, int age) {
this.num = num;
this.age = age;
}
/*
* 重写父类方法:方法名和参数列表要完全一样。
* 要求:
* 子类重写的方法权限要大于父类被重写的方法权限,
* 子类重写的方法的返回值类型要小于父类被重写方法的返回值类型。
* 特点:
* 调用时,创建子类对象,优先使用子类中的重写方法。
* 设计原则: 对于已经投入使用的类,尽量不要进行修改。推荐定义一个新的类,来重复利用其中共性内容,并且添加改动新内容。
* */
@Override//注解可写可不写,用来检测的
public Zi overrideMethod(){
return new Zi();
}
}
com.kdy.test中JavaTest
/*
* 继承。
* */
public class JavaTest {
public static void main(String[] args) {
/*
* new子类时在堆中创建该子类对象的子类空间和父类空间,
* 先构造父类对象(父类的属性和行为)放入父类空间,
* 再构造子类对象(子类的属性和行为)放入子类空间
* */
Zi zi = new Zi();
Fu zi2 = new Zi();//这里new对象是为了举例说明子类属性和父类属性重名的情况,看等号左面的类型决定
System.out.println("=================================");
/*
* 若子类属性名和父类属性名重名,根据变量zi左侧类型决定用子类还是父类的属性
* */
System.out.println(zi.num);//输出子中的num为20
System.out.println(zi2.num);//输出父中的num为10
System.out.println(((Fu) new Zi()).num);//输出父中的num为10
System.out.println(zi.fuNum);//输出父中的fuNum为10
System.out.println(zi2.fuNum);//输出父中的fuNum为10
/*
* 若子类方法和父类方法重名,new的是什么对象,调用的就是谁的方法,只不过若子类没有再找父类方法
* */
zi.fueMethod();//父对象的方法
zi2.fueMethod();//父对象的方法
zi.sameMethod();//子对象的同名方法
zi2.sameMethod();//子对象的同名方法
new Fu().sameMethod();//父对象的同名方法
System.out.println("=================================");
zi.ziMethod();//子对象自己的方法,测试子类内容成员变量num和this.num和super.num的指定。
Fu.staticMethod();//父类直接调用静态方法
Zi.staticMethod();//子类调用父类静态方法
System.out.println("=================================");
/*
* 测试重写方法
* */
System.out.println(zi.overrideMethod() instanceof Zi ? "zi":"fu");
// System.out.println(new Fu().overrideMethod() instanceof Zi ? "zi":"fu");//父类的
// System.out.println(zi2.overrideMethod() instanceof Zi ? "zi":"fu");//父类的
}
}
使用继承写一个简单的发红包程序
com.kdy.domain
public class User {
private String name;//姓名
private int money;//余额
public User(String name, int money) {
this.name = name;
this.money = money;
}
public User() {
}
public void show(){
System.out.println(this.getMoney());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
public class Manager extends User {
public Manager() {
}
public Manager(String name, int money) {
super(name, money);//有参构造,将manager的名称和余额存放在堆中该子对象的父对象父类空间中
}
public ArrayList<Integer> send(int totalMoney, int count) {
ArrayList<Integer> redList = new ArrayList<>();
int leftMoney = super.getMoney();//当前余额
if (totalMoney > leftMoney) {
System.out.println("余额不足");
return redList;//空集合
}
super.setMoney(leftMoney - totalMoney);//扣钱
int avg = totalMoney / count;
int mod = totalMoney % count;//零头
for (int i = 0; i < count-1; i++) {
redList.add(avg);
}
int last = avg+mod;
redList.add(last);//除不开的零头放最后一个红包中
return redList;
}
}
public class Member extends User {
public Member() {
}
public Member(String name, int money) {
super(name, money);//有参构造,将member的名称和余额存放在堆中该子对象的父对象父类空间中
}
public void receive(ArrayList<Integer> list){
int index = new Random().nextInt(list.size());//随机从当前集合获取索引
Integer del = list.remove(index);
int money = super.getMoney();//当前member余额
super.setMoney(money+del);//入账
}
}
com.kdy.test
/*
* 继承:发红包案例
* */
public class JavaTest {
public static void main(String[] args) {
Manager manager = new Manager("群主Name666", 100);
Member one = new Member("成员A", 20);
Member two = new Member("成员B", 10);
Member three = new Member("成员C", 30);
manager.show();
one.show();
two.show();
three.show();
System.out.println("=======================");
ArrayList<Integer> redList = manager.send(20, 3);//扣钱、存入redList
one.receive(redList);//收钱入账
two.receive(redList);
three.receive(redList);
manager.show();
one.show();
two.show();
three.show();
}
}
抽象类
抽象方法:就是加上abstract关键字,然后去掉大括号,直接分号结束。
抽象类:抽象方法所在的类,必须是抽象类才行。在class之前写上abstract即可。抽象类中不一定有抽象方法,可有成员方法、静态方法、构造方法(初始化父类成员使用)...
抽象方法的使用:不能new抽象类,必须有个子类继承这个抽象父类并重写所有抽象方法。
com.kdy.domain
/*
* 抽象类就是普通类加上了个抽象方法,且不能直接new,子类可实现。
* */
public abstract class Animal {
private String name;//成员变量
public static int NUM = 3;//成员变量
public void normalMethod(){
System.out.println("成员方法...");//成员方法
}
public static void staticMethod(){
System.out.println("静态方法...");//静态方法
}
public Animal() {
System.out.println("构造方法");//构造方法
}
public abstract void eat();//抽象方法
}
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
com.kdy.test
/*
* 接口
* */
public class JavaTest {
public static void main(String[] args) {
//Animal animal = new Animal();//抽象类不可直接new
Cat cat = new Cat();
cat.eat();//子类重写的方法
cat.normalMethod();//调用父类成员方法
Cat.staticMethod();//子类调用父类静态方法
}
}
接口
接口是多个类的公共规范,是引用数据类型,编译后仍为.class文件。
接口:jdk7(常量、抽象方法)、jdk8(常量、抽象方法、默认方法、静态方法)、jdk9(常量、抽象方法、默认方法、静态方法,私有方法)。
接口常量:public static final修饰,可以不写即默认。一旦赋值,不可修改。
接口抽象方法:public abstract修饰,可不写即默认。
接口默认方法:public default修饰,可省略public,且有方法体。
接口静态方法:public static,可省略public,且有方法体。
接口不能直接使用,必须由实现类来实现该接口并重写所有的抽象方法。如果实现类没有重写所实现接口的所有抽象方法,那么该实现类就是抽象类才可。
一个类只能继承一个父类,但可实现多接口。一个接口可继承多个接口。
1、实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可。
2、实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类。
3、如果实现类锁实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写,且实现类也要带着default关键字。4、一个类如果直接父类中的方法,和接口中默认方法产生了冲突,优先用父类当中的方法。
com.kdy.test
public interface MyInterface {
int CONSTANT_NUM1 = 666;//接口常量,一旦赋值,不可修改
public static final int CONSTANT_NUM2 = 666;
void method1();
public abstract void method2();
default void defaultMethod(){
System.out.println("默认方法");
}
public static void staticMethod(){
System.out.println("静态方法");
}
}
public class MyInterfaceImpl implements MyInterface{
@Override
public void method1() {
System.out.println("实现接口重写方法1");
}
@Override
public void method2() {
System.out.println("实现接口重写方法2");
}
/*接口中默认方法也可被实现类重写*/
/* @Override
public void defaultMethod() {
System.out.println("实现类中重写接口的默认方法");
}*/
}
/*
* 接口
* */
public class JavaTest {
public static void main(String[] args) {
MyInterfaceImpl myInterfaceImpl = new MyInterfaceImpl();
myInterfaceImpl.method1();//调用方法:new的哪个对象就调用谁的
myInterfaceImpl.method2();
/*调用方法:new的哪个对象就调用谁的。没有再向上找。(实现类中没有就向上找接口中的默认方法)。类似继承中的子类对象方法调用。*/
myInterfaceImpl.defaultMethod();
MyInterface.staticMethod();//接口静态方法
System.out.println(MyInterface.CONSTANT_NUM1);//接口常量
}
}
多态
父类引用指向子类对象:
格式:父类名 对象名 = new 子类名称(); 或 接口名 对象名 = new 实现类名称();
若子类属性名和父类属性名重名,根据变量zi左侧类型决定用子类还是父类的属性。
若子类方法和父类方法重名,new的是什么对象,调用的就是谁的方法,只不过若子类没有再找父类方法。
多态写法等于向上转型(子转父)是安全的,向下转型(父转子)必须是父本来是该子类对象还原
com.kdy.domain
public class Animal {
}
public class Cat extends Animal {
public void catMethod(){
System.out.println("cat~");
}
}
public class Dog extends Animal {
public void dogMethod(){
System.out.println("dogMethod~");
}
}
com.kdy.test
public class JavaTest {
public static void main(String[] args) {
Animal animal = new Cat();
if(animal instanceof Cat){
Cat cat = (Cat)animal;
cat.catMethod();
}else if (animal instanceof Dog){
Dog dog = (Dog)animal;
dog.dogMethod();
}
}
}
接口加多态实现USB接口案例
定义USB接口,具备最基本的开启功能和关闭功能。鼠标和键盘要想能在电脑上使用,那么鼠标和键盘也必须遵守USB规范,实现USB接口,否则鼠标和键盘的生产出来也无法使用。
com.kdy.domain
public interface USB {
public abstract void open();//打开设备
public abstract void close();//关闭设备
}
public class Mouse implements USB {
@Override
public void open() {
System.out.println("打开鼠标");
}
@Override
public void close() {
System.out.println("关闭鼠标");
}
public void click(){
System.out.println("鼠标点击...");
}
}
public class Keyboard implements USB{
@Override
public void open() {
System.out.println("打开键盘");
}
@Override
public void close() {
System.out.println("关闭键盘");
}
public void type(){
System.out.println("键盘按下...");
}
}
public class Computer {
public void powerOn(){
System.out.println("电脑开机");
}
public void powerOff(){
System.out.println("电脑关机");
}
//使用USB设备,接口作为参数,可传入相应实现类——多态写法
public void userDevice(USB usb){
usb.open();//打开设备
if (usb instanceof Mouse){
Mouse mouse = (Mouse)usb;
mouse.click();
}else if(usb instanceof Keyboard){
Keyboard keyboard = (Keyboard)usb;
keyboard.type();
}
usb.close();//关闭设备
}
}
com.kdy.test
public class JavaTest {
public static void main(String[] args) {
Computer computer = new Computer();
computer.powerOn();
Mouse usbMouse = new Mouse();//这样写传入computer.userDevice(usbMouse)会自动转型
computer.userDevice(usbMouse);
USB usbKeyboard = new Keyboard();//这样手动转型多态写法后传入computer.userDevice(usbKeyboard)也可
computer.userDevice(usbKeyboard);
computer.powerOff();
}
}
final关键字
修饰类,和其他类的区别是该类不可被继承,方法也就不可被重写。
修饰方法,该方法不可被覆盖重写,所以final和abstract不可共用。
修饰成员变量:
修饰的其实是栈中的值:对于基本数据类型,一次赋值终不可变;对于引用数据类型,栈中地址值不可改变但是其引用类型的普通成员仍可以通过set进行换数值。
修饰的成员变量必须定义时赋值或通过类构造方法赋值(二者选其一),且所有构造都要赋值,且必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值。
对final修饰成员变量举例:
com.kdy.domain
public class Phone {
private String color;
public Phone(String color) {
this.color = color;
}
public Phone() {
}
public void setColor(String color) {
this.color = color;
}
public String getColor() {
return color;
}
}
public class Person {
public final static String staticNum = "hello666"; //常量
public static String staticNum2 = "hello666"; //静态变量
private final String name/*="zhangsan"*/;//成员变量:需定义时赋值或构造方法赋值,且重载的构造都要赋值
private final Phone phone = new Phone("green");
public Person() {
this.name="zhangsan";
}
public Person(String name) {
this.name=name;
}
public String getName() {//final成员变量只有get方法,无set方法
return name;
}
public Phone getPhone() {//final成员变量只有get方法,无set方法
return phone;
}
}
com.kdy.test
public class JavaTest {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.getName());
System.out.println(person.getPhone().getColor());
person.getPhone().setColor("yellow");
System.out.println(person.getPhone().getColor());
System.out.println(Person.staticNum);
Person.staticNum2 = "hello777";
System.out.println(Person.staticNum2);
}
}
内部类——类中类
成员内部类、局部内部类(含匿名内部类)
演示如下:
com.kdy.domain
/*
* 外部类:public/缺省 class
* 成员内部类:public/protected/缺省/private class
* 局部内部类:缺省 class
* */
public class OutClass {
public String sameNum = "outer member variable";//重名的成员变量
private String outName;
public String getOutName() {
return outName;
}
public void setOutName(String outName) {
this.outName = outName;
}
public class InnerClass{//外部类中的成员内部类
public String sameNum = "outer member variable";//重名的成员变量
private String innerName;
public void innerMethod(){
System.out.println("内部类成员方法");
System.out.println("内部类成员方法直接调用外部类成员变量:"+outName);
}
/*如果内部类的成员变量和外部类的成员变量重名了*/
public void innerMethod2(){
String sameNum="local variable";//重名的成员变量
System.out.println(sameNum);//方法局部变量
System.out.println(this.sameNum);//内部类成员变量,这里的this就近原则指代该类对象
System.out.println(OutClass.this.sameNum);//外部类的成员变量:外部类名.this.成员变量
}
}
public void outMethod(){
System.out.println("外部类成员方法");
System.out.println("外部类成员方法调用内部类成员变量需要创建内部类对象:"+new InnerClass().innerName);
}
public void interInnerMethodByOutMethod(){//测试类中使用成员内部类的方式一:通过外部类的某个方法调用内部类方法
new InnerClass().innerMethod();
}
public void outMethod2(){//外部类成员方法中的局部内部类
int a = 3;
final int b = 5;
int c = 3;
a=3;//二次变值了,局部内部类不能访问了
class PartInner{//局部内部类: 只有当前所属的方法才能使用它,出了这个方法外面就不能用了。
int num = 10 ;
public void partInnerMethod(){
System.out.println(num);
/*局部内部类访问外部类的成员方法的局部变量,该局部变量必须时【有效的final,即不能二次变值】类型
* 因为:方法变量在栈中,new的内容在堆中,方法出栈后变量消失,但堆中的内容还不会立马消失。
* */
//System.out.println(a);
System.out.println(c);
System.out.println(b);
}
}
new PartInner().partInnerMethod();//调用
}
/*
* 匿名内部类:可用于类或方法里
* 如果接口的实现类或父类的子类只需使用唯一的一次,那么这种情况下可以不重新建个文件定义了,改用匿名内部类的形式
* 接口名 对象名 = new 接口名(){//覆盖抽象方法}
* */
MyInterface obj = new MyInterface(){
@Override
public void method() {
System.out.println("匿名内部类~");
}
};
public void outMethod3() {//外部类成员方法中的匿名内部类
obj.method();
MyInterface obj = new MyInterface(){
@Override
public void method() {
System.out.println("匿名内部类~");
}
};
obj.method();
new MyInterface(){//匿名内部类省略对象名就变成了匿名对象,只能在方法中这样写。
@Override
public void method() {
System.out.println("匿名对象~");
}
}.method();
}
}
public interface MyInterface {
void method();
}
com.kdy.test
public class JavaTest {
public static void main(String[] args) {
OutClass outClass = new OutClass();
outClass.interInnerMethodByOutMethod();//测试类中使用成员内部类的方式一:通过外部类的某个方法调用内部类方法
System.out.println("==========================");
//测试类中使用成员内部类的方式二:使用公式
OutClass.InnerClass innerClass = new OutClass().new InnerClass();
innerClass.innerMethod();
}
}
匿名对象(不是匿名内部类)
com.kdy.domian包下:
public class Person {
public String name ;
public void showName(){
System.out.println(this.name);
}
public void hello(){
System.out.println("hello~");
}
}
com.kdy.test包下:
/*
*匿名对象:确定要用到某个对象且仅使用一次
* */
public class JavaTest {
public static void main(String[] args) {
Person person = new Person();
person.name="zhangsan";
person.showName();
new Person().hello();
}
}
类或接口作其他类成员变量
com.kdy.domain
public class Person {
private String name;
private int age;
private Phone phone;
private Skill skill;
public Person() {
}
public Person(String name, int age, Phone phone) {
this.name = name;
this.age = age;
this.phone = phone;
}
public Person(String name, int age, Phone phone,Skill skill) {
this.name = name;
this.age = age;
this.phone = phone;
this.skill = skill;
}
public void show(){//表演才艺
skill.show();
}
}
public class Phone {
private String color;
public Phone() {
}
public Phone(String color) {
this.color = color;
}
}
public interface Skill {
void show();//表演才艺的抽象方法
}
com.kdy.test
public class JavaTest {
public static void main(String[] args) {
Person person = new Person("zhangsan",20,new Phone("yellow"));
Person person2 = new Person("zhangsan", 20, new Phone("yellow"), new Skill() {
@Override
public void show() {
System.out.println("给大家唱个歌吧");
}
});
person2.show();
}
}
接口作为方法的参数或返回值
public class JavaTest {
public static List<String> addNames(List<String> list){
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
list.add("eee");
return list;
}
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list = JavaTest.addNames(list);
System.out.println(list);
}
}
toString
public class JavaTest {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person);//直接打印对象名其实就是打印的对象的toString方法
System.out.println(person.toString());//因为Person没有重写toString方法,调用的是其最终父类Object的toString方法return getClass().getName() + "@" + Integer.toHexString(hashCode())
/*看一个类是否重写了toString,直接打印这个对象即可,没重写的话打印的是地址值*/
}
}
时间日期类
public class JavaTest {
public static void main(String[] args) throws ParseException {
Date date = new Date();//打印系统当前时间 Fri Jul 21 19:07:58 CST 2023
System.out.println(date);
System.out.println(date.getTime());//getTime()打印的是系统当前时间距离元年的毫秒数 1689937742453
Date date1 = new Date(0L);//有参的date对象,将参数转化成距离元年的时间日期 Thu Jan 01 08:00:00 CST 1970
System.out.println(date1);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat sdf2= new SimpleDateFormat("yyyy-MM-dd HH时mm分ss秒");
String dateStr = sdf.format(date);
String dateStr2 = sdf2.format(date);//String转date
System.out.println(dateStr);//2023-07-21
System.out.println(dateStr2);//2023-07-21 19时11分20秒
Date date2 = sdf2.parse(dateStr2);//Date转String
System.out.println(date2);//Fri Jul 21 19:12:16 CST 2023
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.get(Calendar.YEAR)+"--"+calendar.get(Calendar.MONTH));//月份返回西方月份0-11,而不是东方月份1-12.
calendar.set(Calendar.YEAR,9999);
calendar.set(Calendar.MONTH,9);
Date date3 = calendar.getTime();//Calendar转Date
System.out.println(date3);
calendar.setTime(date);//Date转Calendar
System.out.println(calendar.getTime());
Instant instant = date.toInstant();//Date转Instant
System.out.println(instant);//2023-07-21T12:05:32.956Z
Date date4 = Date.from(instant);//Instant转Date
System.out.println(date4);//Fri Jul 21 20:06:43 CST 2023
long currentTimeMillis = System.currentTimeMillis();
System.out.println(currentTimeMillis);//1689941385742
Date date5 = new Date(currentTimeMillis);
System.out.println(date5);//Fri Jul 21 20:09:15 CST 2023
}
}
System.arrayCopy()、Arrays.copyOf
public class JavaTest {
public static void main(String[] args) throws ParseException {
int [] a = {1,2,3,4,5,6,7};
int [] b = {11,12,13,14,15,16,17};
int [] c = {11,12,13,14,15,16,17};
//将a数组复制从下标1开始持续3特元素到b数组的下标1的位置
System.arraycopy(a,1,b,1,3);
System.out.println(Arrays.toString(b));//[11, 2, 3, 4, 15, 16, 17]
int[] d = Arrays.copyOf(a, 3);//复制a数组的前3个元素到新数组
int[] e = Arrays.copyOfRange(a, 3,5);//复制a数组的前下标3到5的元素到新数组,包含from的下标,不包含to的下标
System.out.println(Arrays.toString(d));//[1, 2, 3]
System.out.println(Arrays.toString(e));//[4, 5]
}
}
StringBuilder
长度可变的字符串,在内存中是一个数组,占用内存小,操作效率高:比如进行简单的字符串拼接时就比string内存空间少,操作效率高。
String创建后变成final修饰的常量不可改,进行字符串拼接时占空间效率也低:如String s = "a"+"b"+"c";就会先a+b成ab,后ab+c成c。
StringBuilder底层不被final修饰的长度可变的数组,占空间少、效率高、可扩容。
public class JavaTest {
public static void main(String[] args) throws ParseException {
StringBuilder bu1 = new StringBuilder();
StringBuilder bu2 = new StringBuilder("abc");
System.out.println(bu1);//""
System.out.println(bu2);//"abc"
bu1.append("abc").append(1).append(true).append(8.8).append("中");
System.out.println("abc".toUpperCase().toLowerCase().toUpperCase().toLowerCase());//链式编程:返回值是一个对象可继续调用其方法
System.out.println(bu1);//abc1true8.8中
String str = bu1.toString();//StringBuilder转String
System.out.println(str);//abc1true8.8中
}
}
String与基本数据类型的转换
public class JavaTest {
public static void main(String[] args) throws ParseException {
byte byte1 = 50;
short short1 = 200;
int int1 = 500;
long long1 = 200092233720368547L;//long类型必须加L,不然默认是int类型只不过向上转型成long不报错,但当超出int长度且小于long长度会报错,加上L指定定位long类型
float float1 = 500.005f;//float类型必须加上f结尾,如5就会默认为int,5.5就会默认为double,向下转型成float会报错加上f指定定位float类型
double double1 = 777.99965;
char char1 = 97;//或写成char char1 = 'a';
boolean boolean1 = false;
System.out.println(byte1+"");//50
System.out.println(short1+"");//200
System.out.println(int1+"");//500
System.out.println(long1+"");//200092233720368547
System.out.println(float1+"");//500.005
System.out.println(double1+"");//777.99965
System.out.println(char1+"");//a
System.out.println(boolean1+"");//false
System.out.println("===================================");//封装类toString方法:基本数据类型转String
//打印结果同上
System.out.println(Byte.toString(byte1));
System.out.println(Short.toString(short1));
System.out.println(Integer.toString(int1));
System.out.println(Long.toString(long1));
System.out.println(Float.toString(float1));
System.out.println(Double.toString(double1));
System.out.println(Character.toString(char1));
System.out.println(Boolean.toString(boolean1));
System.out.println("===================================");//封装类的parseXxx方法:String转基本数据类型
System.out.println(Byte.parseByte(Byte.toString(byte1))+12);//62
System.out.println(Short.parseShort(Short.toString(short1))+12);//212
System.out.println(Integer.parseInt(Integer.toString(int1))+12);//512
System.out.println(Long.parseLong(Long.toString(long1))+12);//200092233720368559
System.out.println(Float.parseFloat(Float.toString(float1))+12.5f);//512.505
System.out.println(Double.parseDouble(Double.toString(double1))+12.56);//790.5596499999999
System.out.println((Character.toString(char1)).charAt(0)+2);//99
System.out.println(!Boolean.parseBoolean(Boolean.toString(boolean1)));//true
}
}
Debug
打断点,debug
F8:逐行执行
F7:进入方法 shift+F8:跳出方法
F9:跳到下一断点
ctrl+F2:退出debug结束运行 点击Console切换到控制台
异常
程序的非正常导致JVM非正常停止。
Java中异常是一个类,产生异常就是创建异常对象并抛出,JVM处理异常方式为中断处理。
Throwable:异常根类
Error:继承Throwable
Exception:继承Throwable,包含编译期异常(写代码时的报错)和运行期异常
RuntimeException继承了Exception,java程序运行过程中的问题
com.kdy.test
public class JavaTest {
public static void main(String[] args) throws ParseException {
m1();//使用try-catch处理完异常的直接调用,catch后的代码可正常执行
//m2();//使用throw如果不进行try-catch的话只能一直向上抛异常,抛给main方法,交给JVM进行处理,即使main方法没有抛出的异常,运行期间报错也会交给JVM中断处理
//getElement(null, 3);//运行期异常,可用这里不用处理,交给JVM打印中断处理
//readFile("c:\\b.txt");//调用的该方法抛出了译期异常,需要main方法继续抛出给JVM或try-catch
try {
readFile("d://d.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw, true));//拿到异常信息到StringWriter
String str = sw.toString();
System.out.println(str);
}
m3();//多个异常一次捕获,多次处理
m4();//多个异常一次捕获,一次处理
}
/*
* 使用try-catch捕获处理掉异常
* */
public static void m1() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
Date date = sdf.parse("1999-0909");//可能出现异常的代码
} catch (ParseException e) {
e.printStackTrace();//异常处理的逻辑
}
System.out.println("后续代码1");
int[] a = {1, 2, 3};
try {
int a3 = a[3];
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();//异常处理的逻辑
}
System.out.println("后续代码2");
}
/*
* 使用throws向上抛,交由方法的调用者处理,如这里的main方法,如果main当方法中没有进一步try-catch,就会交由JVM进行打印中断处理
* */
public static void m2() throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse("1999-0909");
System.out.println("后续代码");
}
/*
* throw来抛出一个异常
* throw new Exception("产生异常的原因")
* 必须写在方法内部,throw的对象必须时Exception或其子类
* throw抛出异常后,我们必须进行处理:若是RunTimeException获取子对象,我们可用交由JVM处理进行中断打印异常信息
* 若是编译器异常,我们需要通过try-catch或throws继续向上抛
* */
//如下面这个NullPointerException是运行期异常,可用不用处理:
public static int getElement(int[] arr, int index) throws ParseException {
if (arr == null) {
throw new NullPointerException("传递的数组为null");
} else {
return arr[index];
}
}
//再如FileNotFoundException是编译器异常,必须处理才能通过编译
public static void readFile(String fileName) throws FileNotFoundException {
if (!"c:\\a.txt".equals(fileName)) {
throw new FileNotFoundException("传递的文件路径不是c:\\a.txt");
}
}
/*
*多个异常一次捕获,多次处理
* 注意:当存在异常子父类关系时,父类异常写在子类异常下面(原因:抛出的异常对象,会从上到下依次赋值给catch中定义的异常变量)
* */
public static void m3() {
try {
int[] arr = {1, 2, 3};
System.out.println(arr[3]);//数组下标越界ArrayIndexOutOfBoundsException
List<Integer> list = Arrays.asList(1, 2, 3);
System.out.println(list.get(3));//数组下标越界ArrayIndexOutOfBoundsException
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("1");
arrayList.add("2");
arrayList.add("3");
System.out.println(arrayList.get(3));//索引越界异常IndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println(e);
e.printStackTrace();
} catch (IndexOutOfBoundsException e) {
System.out.println(e);
e.printStackTrace();
}
}
/*
*多个异常一次捕获,一次处理
* //final中不管抛没抛异常,都会执行到,避免在这里写return。
* */
public static void m4() {
try {
int[] arr = {1, 2, 3};
System.out.println(arr[3]);//数组下标越界ArrayIndexOutOfBoundsException
List<Integer> list = Arrays.asList(1, 2, 3);
System.out.println(list.get(3));//数组下标越界ArrayIndexOutOfBoundsException
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("1");
arrayList.add("2");
arrayList.add("3");
System.out.println(arrayList.get(3));//索引越界异常IndexOutOfBoundsException
} catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}finally {
//final中不管抛没抛异常,都会执行到,避免在这里写return。
}
}
/*
*继承时的异常
*父类中定义的方法且定义throw异常,其子类在继承父类重写父类方法时,要抛出父类相同异常或子类异常或不抛出异常
* */
}
com.kdy.domain
/*
* 如果父类中定义的方法且定义throw异常,其子类在继承父类重写父类方法时,要抛出父类相同异常或子类异常或不抛出异常
* 父类成员方法没有throw异常,子类也不能抛出异常,只能try-catch
* 原因:多态写法时,向下转型可顺利进行
* */
public class Fu {
public void show1() throws NullPointerException, ClassCastException {}
public void show2() throws IndexOutOfBoundsException {}
public void show3() throws IndexOutOfBoundsException {}
public void show4() {}
}
class Zi extends Fu {
//子类重写父类方法时,抛出和父类相同的异常
@Override
public void show1() throws NullPointerException, ClassCastException {}
//子类重写父类方法时,抛出和父类异常的子类
@Override
public void show2() throws ArrayIndexOutOfBoundsException {}
//子类重写父类方法时,不抛出异常
@Override
public void show3() {}
//父类成员方法没有throw异常,子类也不能抛出异常,只能try-catch
@Override
public void show4() {
try {
throw new Exception("编译期异常");
} catch (Exception e) {
e.printStackTrace();
}
}
}
com.kdy.exception
/*
* 自定义异常类:必须继承Exception或RunTimeException,且必须重写有参无参构造,命名一般以Exception结尾
* 继承Exception编译期异常必须处理,继承RunTimeException运行期异常,可不用处理交由JVM
* */
public class RegisterException extends Exception{
public RegisterException() {
//super();//super()可省略不写,也是先调父类构造
}
public RegisterException(String message) {//所有异常类一般都有这个有参构造,调用父类异常信息构造,交由父类处理这个异常信息
super(message);
}
}
class Test{
static String [] usernames = {"张三","李四","王五"};
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入要注册的用户名:");
String username = sc.next();
checkUsername(username);
}
public static void checkUsername(String username){
for (String name : usernames ){
if (name.equals(username)){
// throw new RegisterException("该用户名已被注册");//通过方法throws出
try {
throw new RegisterException("该用户名已被注册");//或通过try-catch自己处理
} catch (RegisterException e) {
e.printStackTrace();
return;//自己处理后,要实现业务效果需要这里直接结束方法。
}
}
}
System.out.println("注册成功");
}
}
多线程
并发与并行
并发︰指两个或多个事件在同一个时间段内发生,快速交替进行。
并行∶指两个或多个事件在同一时刻发生(同时发生)。
线程与进程
进程︰是指一个内存中运行的应用程序(如运行QQ.exe时qq进程就进入到内存了,在任务管理器中可看到,结束进程就把该程序占用的内存强制清空了),每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
线程∶线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。线程是操作系统计算的最小单位
简而言之︰一个程序运行后至少有一个进程,一个进程中可以包含多个线程
cpu核数和线程数、多线程
cpu可指挥电脑中软硬件干活,4核8线程的cpu能同时进行8个线程任务。最小的执行单元线程任务。单核单线程的cpu如果用户点击电脑很多程序一起运行cpu就会在这些进程的非常多的线程任务之间以1/n毫秒的速度高速切换。4核8线程就会提高8倍的效率。
举例如果用户使用360管家,这就是开启一个进程,进入到内存中,同时执行病毒查杀、清理垃圾和电脑加速的话,就是同时执行了三个线程,单核单线程的cpu会高速切换处理,4核8线程的cpu效率更高。
多线程的好处:效率高,各线程之间互不影响,可让cpu的使用率更高。
线程调度
分时调度:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU 的时间。
抢占式调度:优先让优先级高的线程使用CPU,如果线释的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
可在任务管理器-详细信息中设置现成的优先级。
main线程
JVM执行main方法时的主线程。
单线程
单线程异常JVM中断处理后,异常代码后面代码无法继续执行,
创建多线程——继承Thread类
/*
* 实现多线程方式一:继承Thread类并重写run方法
* 使用该子类对象的start方法开启线程
* 多个线程有自己独立占空间,互不影响
* 所有的线程对象都必须是Thread类或其子类的实例
* */
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread线程getName:"+getName());//当前线程名 MyThread线程getName:Thread-0 。setName后:MyThread线程getName:线程666
System.out.println("MyThread线程:"+Thread.currentThread().getName());//获取当前线程名 MyThread线程:线程666
for (int i = 0; i < 20; i++) {
System.out.println("run:"+i);
}
}
public MyThread() {//默认super();
}
public MyThread(String name) {
super(name);
}
}
class Test{
public static void main(String[] args) {
System.out.println("main线程:"+Thread.currentThread());//获取当前线程名 main线程:Thread[main,5,main]
MyThread myThreadWithName = new MyThread("线程777");//需要在MyThread中重写有参构造调用父类有参构造
System.out.println(myThreadWithName.getName());//线程777
MyThread myThread = new MyThread();
System.out.println("myThread线程getName:"+myThread.getName());//当前线程名 myThread线程getName:Thread-0
myThread.setName("线程666");
System.out.println(myThread.getName());//线程666
myThread.start();//开启的Thread子类重写run方法的线程
for (int i = 0; i < 20; i++) {//main线程
System.out.println("main:"+i);
}
/*结果:两个线程抢夺cpu执行时间,结果就看到是交替进行
* main:0 main:1 main:2 main:3main:4main:5main:6main:7main:8main:9run:0run:1run:2run:3run:4run:5run:6run:7run:8run:9run:10run:11run:12run:13run:14run:15run:16run:17run:18run:19main:10main:11main:12main:13main:14main:15main:16main:17main:18main:19
* */
/*线程休眠*/
try {
Thread.sleep(5000);//线程休眠5000毫秒——5秒种
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main线程休眠后的代码");
}
}
创建多线程——实现Runnable接口
/*
* 实现多线程方式二:实现Runnable接口,并实现里面的run方法
* 通过创建该Runnable接口实现类对象作为创建Thread对象的参数,再调用Thread对象的start即可
* Runnable接口创建线程的好处,可以继续继承其他的父类
* */
public class RunnableImpl implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
class Test6{
public static void main(String[] args) {
RunnableImpl runnable = new RunnableImpl();
Thread thread = new Thread(runnable);
thread.start();
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
/*
* 匿名内部类创建线程 new 父类
* */
new Thread(){ //省略了对象名,其实也可称为匿名对象
@Override
public void run(){
System.out.println("匿名内部类线程new父类");
}
}.start();
/*
* 匿名内部类创建线程 new 接口
* */
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类线程new接口");
}
};
new Thread(r).start();
}
}
线程安全问题
多个线程访问共享数据会出现线程安全问题。
public class TicketThreadRunnableImpl implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票...");
ticket--;
}
}
}
}
/*
* 多个线程t1、t2、t3访问共享资源r中的成员属性时,就会发生线程安全问题
* */
class Test666{
public static void main(String[] args) {
TicketThreadRunnableImpl r = new TicketThreadRunnableImpl();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
t1.start();
t2.start();
t3.start();
//出现了重复卖票,无顺序(当切换到另一个线程开始买卖票,卖的第一张都比上一个被切换的线程的最后一张序号大,)
}
}
解决线程安全问题
1.同步代码块。2.同步方法。3.锁机制。
/*
* 使用同步代码块
* 1.通过代码块中的锁对象,可以使用任意的对象。2.但是必须保证多个线程使用的锁对象是同一个。3.锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行
* 同步代码块中的内容将被锁住,拿到锁的线程若还没执行完同步代码块中的内容被其他线程抢占了cpu,其他线程没有锁也不能执行只能最终cu有把执行权给了有锁对象的线程去执行,等待其执行完同步代码块中的内容后释放锁对象,其他线程接着抢锁对象。
* */
public class TicketThreadRunnableImpl implements Runnable {
Object obj = new Object();//创建锁对象
private int ticket = 100;
@Override
public void run() {
while (true) {
synchronized (obj) {//使用同步代码块
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票...");
ticket--;
}
}
}
}
}
/*
* 同步代码块解决线程安全问题
* */
class Test666{
public static void main(String[] args) {
TicketThreadRunnableImpl r = new TicketThreadRunnableImpl();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
t1.start();
t2.start();
t3.start();
}
}
/*
* 使用同步方法
* 1.把访问了共享数据的代码抽取出来,放到一个方法中。2.在方法上添加synchronized修饰符。
* 同步方法的锁对象是这个线程的实现类,也就是this.
* */
public class TicketThreadRunnableImpl implements Runnable {
private static int ticketStatic = 100;
private int ticket = 100;
@Override
public void run() {
while (true) {
payTicket();
}
}
public synchronized void payTicket() {//同步方法的锁对象是这个线程的实现类,也就是this.
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票...");
ticket--;
}
}
public synchronized static void payTicket2() {//静态同步方法的锁对象是本类的class文件对象.
if (ticketStatic > 0) {
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticketStatic + "张票...");
ticketStatic--;
}
}
}
/*
* 同步方法解决线程安全问题
* */
class Test666 {
public static void main(String[] args) {
TicketThreadRunnableImpl r = new TicketThreadRunnableImpl();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
t1.start();
t2.start();
t3.start();
}
}
/*
* 使用Lock锁
* 1.在成员位置创建一个ReentrantLock对象。2.在可能会出现安全问题的代码前调用Lock接口中的方法Lock获取锁。3.在可能会出现安全问题的代码后调用Lock接口中的方法unLock释放锁
* */
public class TicketThreadRunnableImpl implements Runnable {
private int ticket = 100;
Lock l = new ReentrantLock();//在成员位置创建一个ReentrantLock对象
@Override
public void run() {
while (true) {
l.lock();//在可能会出现安全问题的代码前调用Lock接口中的方法Lock获取锁
if (ticket > 0) {
try {
Thread.sleep(10);//提高安全问题出现的概率,让程序睡眠
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票...");
ticket--;
}
l.unlock();//在可能会出现安全问题的代码后调用Lock接口中的方法unLock释放锁
}
}
}
/*
* Lock锁解决线程安全问题
* */
class Test666 {
public static void main(String[] args) {
TicketThreadRunnableImpl r = new TicketThreadRunnableImpl();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
t1.start();
t2.start();
t3.start();
}
}
线程间通信
多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。
比如∶线程A用来生成包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的动作,一个是生产,一个是消费,那么线程A与线程B之间就存在线程通信问题。
为什么要处理线程间通信:
多个线程并发执行时,在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且我们希望他们有规律的执行,那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据。
如何保证线程间通信有效利用资源:
多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变星的使用或操作。就是多个线程在操作同一份数据时,避免对同一共享变量的争夺。也就是我们需要通过一定的手段使各个线程能有效的利用资源。而这种手段即――等待唤醒机制。wait/notify就是线程间的一种协作机制。
wait/notify:
就是在一个线程进行了规定操作后,就进入等待状态( wait()),等待其他线程执行完他们的指定代码过后再将其唤醒( notify() );在有多个线程进行等待时,如果需要,可以使用notifyAll()来唤醒所有的等待线程。
注意:线程被唤醒后,还需要持有锁才能执行。且wait()与notify()必须是一个锁对象才可。
注:
阻塞状态:具有cpu的执行资格,等待cpu空闲时执行
休眠状态:放弃cpu的执行资格,cpu空闲,也不执行
挂起和阻塞不同:
阻塞仍在内存,挂起换出到磁盘中了。
阻塞一般等待资源(IO、CPU、信号量)时发生,挂起一般是由于用户或系统的需要。
阻塞在资源得到时(如获取了锁)后会就绪,挂起一般在时机合适时主动激活。
线程通信案例--包子铺
public class JavaTest {
public static void main(String[] args) throws ParseException {
Object obj = new Object();
new Thread(){
@Override
public void run(){//顾客线程
while (true){
synchronized (obj){
System.out.println("顾客线程告知老板线程需要的包子和数量");
try{
obj.wait();//等待老板线程做包子,中断等待老板线程做好包子后唤醒继续后续代码
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("顾客线程等到老板线程做好包子的通知唤醒,开始吃包子");
}
/* try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
}
}.start();
new Thread(){
@Override
public void run(){//老板线程
while (true){
try {
Thread.sleep(3000);//花3秒中做包子
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj){
System.out.println("老板线程做好包子了,唤醒顾客线程,通知顾客开始吃了");
obj.notify();//唤醒obj锁对象中被锁住的线程,即上面的顾客线程之前调用了obj.wait()现在唤醒的是它
}
}
}
}.start();
/*
* 这个基础案例,是先顾客线程wait(只能notify唤醒,cpu即使空闲也执行不到),wait时释放锁,老板线程拿到锁和cpu执行权后
* 老板线程先sleep(只能等到计时结束,即使cpu空闲也不执行),sleep3秒后,再notify唤醒顾客线程,顾客线程被唤醒后需等待老板线程的synchronized执行完释放锁后,
* 顾客线程执行后面打印语句,顾客线程synchronized方法执行完后,释放锁资源。这时顾客线程和老板线程随机抢占锁资源和cpu执行权,
* 但老板线程抢到cpu后先sleep3秒再抢锁,就都让给了顾客线程锁和cpu执行权。
* */
}
}
wait会释放锁,synchronized结束后也会释放锁,sleep不会释放锁仅仅休眠一些时间,notify唤醒其他线程也需要拿到锁后才能执行。
包子铺案例升级版
com.kdy.domain
//仅作为锁对象和提供当前包子的信息和状态的工具对象使用,并不是实际包子个数
public class BaoZi {
String pi;
String xian;
boolean flag = false;//包子状态
}
public class BaoZiPu extends Thread{
private BaoZi bz;
public BaoZiPu(BaoZi bz) {
this.bz = bz;
}
@Override
public void run(){
int count = 0;
while (true){
synchronized (bz){//同步代码块
if (bz.flag==true){//如果 有包子,包子铺线程就使用包子对象作为锁进行wait
try {
bz.wait();//释放锁,等待其他线程拿到锁后执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}//吃货线程把bz的状态改为false,且把本线程唤醒了,从wait的地方也就是这里继续往下执行
if (count%2==0){//赋值一些信息
bz.pi="薄皮";
bz.xian="三鲜馅";
}else{
bz.pi="冰皮";
bz.xian="猪肉大葱馅";
}
count++;
System.out.println("包子铺正在生成:"+bz.pi+bz.xian+"的包子");
try {
Thread.sleep(3000);//本线程生成包子时先sleep3秒钟,这时吃货线程还是wait没有被唤醒就不执行,本线程计时结束本线程方可继续执行
}catch (InterruptedException e){
e.printStackTrace();
}
bz.flag=true;//包子铺生成好后,修改bz信息状态为true,且唤醒正在等待的吃货线程
bz.notify();//唤醒bz对象作为锁的此时正在wait的线程,目前是吃货线程刚刚bz.wait了.等待该synchronized结束释放锁后,被唤醒的线程抢到锁后就可以执行了。
System.out.println("包子铺生产好了:"+bz.pi+bz.xian+"的包子,吃货可以吃了");
}
}
}
}
public class ChiHuo extends Thread {
private BaoZi bz;
public ChiHuo(BaoZi bz) {
this.bz = bz;
}
@Override
public void run() {
while (true) {
synchronized (bz) {//同步代码块
if (bz.flag == false) {
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {//如果包子铺判断的包子状态为true,包子铺wait了,本吃货线程就可拿到锁后执行到该else了
System.out.println("吃货正在吃:" + bz.pi + bz.xian + "的包子");
bz.flag = false;
bz.notify();//吃完了,修改bz状态为false,唤醒包子铺线程,本synchronized执行完就会释放锁给被唤醒的包子铺线程执行了
System.out.println("吃货已吃完" + bz.pi + bz.xian + "的包子,包子铺开始生成包子");
System.out.println("----------------------------");
}
}
}
}
}
com.kdy.test
public class JavaTest {
public static void main(String[] args) throws ParseException {
BaoZi bz = new BaoZi();
new BaoZiPu(bz).start();
new ChiHuo(bz).start();
}
}
线程池
线程池其实就是底层为集合的容器。线程池中可用有多个线程,以便任务队列中有多个任务时,可从这线程池获得线程对象,执行任务。
任务队列中任务太多了需要等待,任务队列中执行完任务的线程会回归到线程池,任务队列中其他等待的任务再从线程池中获取线程对象执行任务。
合理利用线程池能够带来三个好处:
1.降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
⒉.提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
3.提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。
com.kdy.domain
public class RunnableImpl implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
com.kdy.test
public class JavaTest {
public static void main(String[] args) throws ParseException {
ExecutorService es = Executors.newFixedThreadPool(3);//创建执行线程数位3的线程池
//线程池中初始默认的线程名命名为:pool-1-thread-1 pool-1-thread-2 pool-1-thread-3
es.submit(new RunnableImpl());//创建一个新的线程执行
//线程池会一直开启,使用完了的线程会归还给线程池,线程池可继续使用
es.submit(new RunnableImpl());//创建一个新的线程执行
es.submit(new RunnableImpl());//创建一个新的线程执行
es.submit(new RunnableImpl());//创建一个新的线程执行
es.submit(new RunnableImpl());//创建一个新的线程执行
es.submit(new RunnableImpl());//创建一个新的线程执行
//销毁线程池(不建议使用)
es.shutdown();
}
}