目录
基本数据类型
数据类型 | 关键字 | 内存占用 | 取值范围 |
整数 | byte | 1 | -128 ~ 127 |
short | 2 | -2^15 ~ 2^15-1(-32768~32767) | |
int | 4 | -2^31 ~ 2^31-1 | |
long | 8 | -2^63 ~ 2^63-1 | |
浮点数 | float | 4 | 1.401298e-45 ~ 3.402823e+38 |
double | 8 | 4.9000000e-324 ~ 1.797693e+308 | |
字符 | char | 2 | 0-65535 |
布尔 | boolean | 1 | true , false |
-
如果要定义 一个整数类型的变量,不知道选择哪种数据类型了,默认使用int。
-
如果要定义 一个小数类型的变量,不知道选择哪种数据类型了,默认使用double。
-
如果要定义一个long类型的变量,那么在数据值的后面需要加上L后缀。(大小写都可以,建议大写。)
容量小的类型自动转换为容量大的数据类型;
数据类型按容量大小排序为: byte, short, char -> int -> long -> float ->double byte, short, char之间不会相互转换,他们三者在计算时首先转换为int类型
标识符
- 必须由数字、字母、下划线_、美元符号$组成。
- 数字不能开头
- 不能是关键字
- 区分大小写
数组
两种定义方式:数据类型 [] 数组名,比如:int [] array;
数据类型 数组名 [],比如: int array []。
静态初始化:数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3,元素4...};
比如:int[] arr = new int[]{11,22,33};
double[] arr = new double[]{1.1,1.2,1.3};
简化格式:数据类型[] 数组名 = {元素1,元素2,元素3,元素4...};
比如:int[] array = {1,2,3,4,5};
double[] array = {1.1,1.2,1.3};
动态初始化:数据类型[] 数组名 = new 数据类型[数组的长度];
比如:1.定义一个数组,存3个人的年龄,年龄未知 int[] agesArr = new int[3];
2.定义一个数组,存班级10名学生的考试成绩,考试成绩暂时未知,考完才知道。
int[] scoresArr = new int[10];
方法
method,程序中最小的执行单元。(其实就是函数)
无参数方法:
定义格式:
public static void 方法名 () {
//方法体;
}
调用格式:
方法名();
方法必须先定义,后调用,否则程序将报错。
带参数方法:
定义格式:
public static void 方法名 (参数1,参数2,参数3...) {
//方法体;
}
调用格式:
方法名(参数1,参数2,参数3...);
方法调用时,参数的数量与类型必须与方法定义中的设置相匹配,否则程序将报错。
带返回值方法:
定义格式:
public static 数据类型 方法名 (参数) {
//方法体;
}
调用格式:
方法名(参数);
数据类型 变量名 = 方法名(参数);
isEvenNumber ( 5 ) ;
boolean flag = isEvenNumber ( 5 );
方法的返回值通常会使用变量接收,否则该返回值将无意义。
注意事项:
1.方法不能嵌套定义
2.void表示无返回值,可以省略return,也可以单独的书写return,后面不加数据
方法重载
方法重载指同一个类中定义的多个方法之间的关系,满足下列条件的多个方法相互构成重载
- 多个方法在同一个类中
- 多个方法具有相同的方法名
- 多个方法的参数不相同,类型不同或者数量不同
注意:
-
重载仅对应方法的定义,与方法的调用无关,调用方式参照标准格式
-
重载仅针对同一个类中方法的名称与参数进行识别,与返回值无关,换句话说不能通过返回值来判定两个方法是否相互构成重载
注:
System.out.println(“内容”); 输出内容并换行
System.out.print(“内容”); 输出内容不换行
System.out.println(); 起到换行的作用
面向对象
类和对象的关系
- 类:类是对现实生活中一类具有共同属性和行为的事物的抽象
- 对象:是能够看得到摸的着的真实存在的实体
- 简单理解:**类是对事物的一种描述,对象则为具体存在的事物
类的定义:
- 属性:在类中通过成员变量来体现(类中方法外的变量)
- 行为:在类中通过成员方法来体现(和前面的方法相比去掉static关键字即可)
public class 类名 {
// 成员变量
变量1的数据类型 变量1;
变量2的数据类型 变量2;
…
// 成员方法
方法1;
方法2;
}
对象的使用:
创建对象
格式:类名 对象名 = new 类名();
范例:Phone p = new Phone();
使用对象
1:使用成员变量
格式:对象名.变量名
范例:p.brand
2:使用成员方法
格式:对象名.方法名()
范例:p.call()
public class PhoneDemo {
public static void main(String[] args) {
//创建对象
Phone p = new Phone();
//使用成员变量
System.out.println(p.brand);
System.out.println(p.price);
p.brand = "小米";
p.price = 2999;
System.out.println(p.brand);
System.out.println(p.price);
//使用成员方法
p.call();
p.sendMessage();
}
}
多个对象在堆内存中,都有不同的内存划分,成员变量存储在各自的内存区域中,成员方法多个对象共用的一份。
成本变量和局部变量:
-
类中位置不同:成员变量(类中方法外)局部变量(方法内部或方法声明上)
-
内存中位置不同:成员变量(堆内存)局部变量(栈内存)
-
生命周期不同:成员变量(随着对象的存在而存在,随着对象的消失而消失)局部变量(随着方法的调用而存在,醉着方法的调用完毕而消失)
-
初始化值不同:成员变量(有默认初始化值)局部变量(没有默认初始化值,必须先定义,赋值才能使用)
封装:
是面向对象三大特征之一(封装,继承,多态)
对象代表什么,就得封装对应的数据,并提供数据对应的行为。
封装代码实现:将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问;
成员变量private,提供对应的getXxx()/setXxx()方法。
构造方法:
创造对象 Student stu = new Student();
class Student {
private String name;
private int age;
//构造方法
public Student() {
System.out.println("无参构造方法");
}
public void show() {
System.out.println(name + "," + age);
}
}
/*
测试类
*/
public class StudentDemo {
public static void main(String[] args) {
//创建对象
Student s = new Student();
s.show();
}
}
构造方法的创建:如果没有定义构造方法,系统将给出一个默认的无参数构造方法;如果定义了构造方法,系统将不再提供默认的构造方法。
构造方法的重载:如果自定义了带参构造方法,还要使用无参数构造方法,就必须再写一个无参数构造方法。
推荐的使用方式:无论是否使用,都手工书写无参数构造方法。
重要功能:可以使用带参构造,为成员变量进行初始化。
键盘录入
next(),nextline():接受任何数据,但都会返回一个字符串。
nextInt():只能接受整数。
nextDouble():能接收整数和小数,但是都会看做小数返回。录入字母会报错。
next(),nextInt(),nextDouble()在接收数据的时候,会遇到空格,回车,制表符其中一个就会停止接收数据。但是这些符号 + 后面的数据还在内存中并没有接收。如果后面还有其他键盘录入的方法,会自动将这些数据接收。
nextLine()方法是把一整行全部接收完毕。
键盘录入分为两套:
- next()、nextInt()、nextDouble()这三个配套使用。
如果用了这三个其中一个,就不要用nextLine()。
- nextLine()单独使用。
如果想要整数,那么先接收,再使用Integer.parseInt进行类型转换。
类型转换示例:
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();//键盘录入123
System.out.println("此时为字符串" + s);//此时123是字符串
int i = Integer.parseInt(s);//想要整数再进行转换
System.out.println("此时为整数:" + i);
API
API (Application Programming Interface) :应用程序编程接口
java中的API:指的就是 JDK 中提供的各种功能的 Java类,这些类将底层的实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可,我们可以通过帮助文档来学习这些API如何使用。
String类
String 类代表字符串,Java 程序中的所有字符串文字(例如“abc”)都被实现为此类的实例。也就是说,Java 程序中所有的双引号字符串,都是 String 类的对象。String 类在 java.lang 包下,所以使用的时候不需要导包。
-
字符串不可变,它们的值在创建后不能被更改
-
虽然 String 的值是不可变的,但是它们可以被共享
-
字符串效果上相当于字符数组( char[] ),但是底层原理是字节数组( byte[] )
常见构造方法:
public class StringDemo01 {
public static void main(String[] args) {
//public String():创建一个空白字符串对象,不含有任何内容
String s1 = new String();
System.out.println("s1:" + s1);
//public String(char[] chs):根据字符数组的内容,来创建字符串对象
char[] chs = {'a', 'b', 'c'};
String s2 = new String(chs);
System.out.println("s2:" + s2);
//public String(byte[] bys):根据字节数组的内容,来创建字符串对象
byte[] bys = {97, 98, 99};
String s3 = new String(bys);
System.out.println("s3:" + s3);
//String s = “abc”; 直接赋值的方式创建字符串对象,内容就是abc
String s4 = "abc";
System.out.println("s4:" + s4);
}
}
两种创建字符串对象方式:
-
通过构造方法创建
通过 new 创建的字符串对象,每一次 new 都会申请一个内存空间,虽然内容相同,但是地址值不同。
-
直接赋值方式创建
以“”方式给出的字符串,只要字符序列相同(顺序和大小写),无论在程序代码中出现几次,JVM 都只会建立一个 String 对象,并在字符串池中维护。
字符串比较:
==号的作用:
-
比较基本数据类型:比较的是具体的值
-
比较引用数据类型:比较的是对象地址值
equals方法:
public boolean equals(String s) 比较两个字符串内容是否相同、区分大小写
public class StringDemo02 {
public static void main(String[] args) {
//构造方法的方式得到对象
char[] chs = {'a', 'b', 'c'};
String s1 = new String(chs);
String s2 = new String(chs);
//直接赋值的方式得到对象
String s3 = "abc";
String s4 = "abc";
//比较字符串对象地址是否相同
System.out.println(s1 == s2);//false
System.out.println(s1 == s3);//false
System.out.println(s3 == s4);//true
System.out.println("--------");
//比较字符串内容是否相同
System.out.println(s1.equals(s2));//true
System.out.println(s1.equals(s3));//true
System.out.println(s3.equals(s4));//true
}
}
StringBuilder:看作一个容器,创建后里面的内容是可变的。
public class StringBuilderDemo3 {
public static void main(String[] args) {
//1.创建对象
StringBuilder sb = new StringBuilder("abc");
//2.添加元素
/*sb.append(1);
sb.append(2.3);
sb.append(true);*/
//反转
sb.reverse();
//获取长度
int len = sb.length();
System.out.println(len);
//打印
//普及:
//因为StringBuilder是Java已经写好的类
//java在底层对他做了一些特殊处理。
//打印对象不是地址值而是属性值。
System.out.println(sb);
}
}
链式编程:
public class StringBuilderDemo4 {
public static void main(String[] args) {
//1.创建对象
StringBuilder sb = new StringBuilder();
//2.添加字符串
sb.append("aaa").append("bbb").append("ccc").append("ddd");
System.out.println(sb);//aaabbbcccddd
//3.再把StringBuilder变回字符串
String str = sb.toString();
System.out.println(str);//aaabbbcccddd
}
}
StringJoiner
-
StringJoiner跟StringBuilder一样,也可以看成是一个容器,创建之后里面的内容是可变的。
-
作用:提高字符串的操作效率,而且代码编写特别简洁,但是目前市场上很少有人用。
-
JDK8出现的
//1.创建一个对象,并指定中间的间隔符号
StringJoiner sj = new StringJoiner("---");
//2.添加元素
sj.add("aaa").add("bbb").add("ccc");
//3.打印结果
System.out.println(sj);//aaa---bbb---ccc
//1.创建对象
StringJoiner sj = new StringJoiner(", ","[","]");
//2.添加元素
sj.add("aaa").add("bbb").add("ccc");
int len = sj.length();
System.out.println(len);//15
//3.打印
System.out.println(sj);//[aaa, bbb, ccc]
String str = sj.toString();
System.out.println(str);//[aaa, bbb, ccc]
ArrayList类 集合
长度可变;添加数据的时候不需要考虑索引,默认将数据添加到末尾。
只能存储引用数据类型。
常用方法:
构造方法:
public ArrayList();创建一个空的集合对象
成员方法:
public boolean add(要添加的元素) 将指定的元素追加到此集合的末尾
public boolean remove(要删除的元素) 删除指定元素,返回值表示是否删除成功
public E remove(int index) 删除指定索引处的元素,返回被删除的元素
public E set(int index,E element) 修改指定索引处的元素,返回被修改的元素
public E get(int index) 返回指定索引处的元素
public int size() 返回集合中的元素的个数
static
static是静态的意思。 static可以修饰成员变量或者修饰成员方法
被static修饰的成员变量,叫做静态变量。被该类的所有对象共享。
方法同上。可以直接通过类名.xxx进行访问。
1.当 static
修饰成员变量或者成员方法时,该变量称为静态变量,该方法称为静态方法。该类的每个对象都共享同一个类的静态变量和静态方法。任何对象都可以更改该静态变量的值或者访问静态方法。但是不推荐这种方式去访问。因为静态变量或者静态方法直接通过类名访问即可,完全没有必要用对象去访问。
2.无static修饰的成员变量或者成员方法,称为实例变量,实例方法,实例变量和实例方法必须创建类的对象,然后通过对象来访问。
3.static修饰的成员属于类,会存储在静态区,是随着类的加载而加载的,且只加载一次,所以只有一份,节省内存。存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。它优先于对象存在,所以,可以被所有对象共享。
4.无static修饰的成员,是属于对象,对象有多少个,他们就会出现多少份。所以必须由对象调用。
继承
假如多个类中存在相同属性和行为时,我们可以将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可。
多个类可以称为子类,单独被继承的那一个类称为父类、超类(superclass)或者基类。
继承就是子类继承父类的属性和行为,使得子类对象可以直接具有与父类相同的属性、相同的行为。子类可以直接访问父类中非私有的属性和行为。
好处:1.提高代码的复用性(减少代码冗余、相同代码重复利用)
2.使类与类之间产生了关系
extends关键字继承,并且Java是单继承的,一个类只能继承一个直接父类。
class 父类 {
...
}
class 子类 extends 父类 {
...
}
并不是父类所有的内容都可以给子类继承的:
子类不能继承父类的构造方法。
子类可以继承父类的私有成员(成员变量、方法),只是子类无法直接访问而已,可以通过getter/setter方法访问父类的private成员变量。
子父类中出现了同名的成员变量时,子类会优先访问自己对象中的成员变量。
想访问父类的话,要用到super关键字;
super.父类成员变量名
eg.
// 访问子类中的num
System.out.println("Zi num=" + this.num);
// 访问父类中的num
System.out.println("Fu num=" + super.num);
如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。
如果子类父类出现重名的成员方法,则创建子类对象调用该方法的时候,子类对象会优先调用自己的方法。
方法重写:子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。
@Override:注解,重写注解校验!
这个注解标记的方法,就说明这个方法必须是重写父类的方法,否则编译阶段报错。
建议重写都加上这个注解,一方面可以提高代码的可读性,一方面可以防止重写出错!
加上后的子类代码形式如下:
public class Cat extends Animal {
// 声明不变,重新实现
// 方法名称与父类全部一样,只是方法体中的功能重写写了!
@Override
public void cry(){
System.out.println("我们一起学猫叫,喵喵喵!喵的非常好听!");
}
}
-
子类构造方法执行的时候,都会在第一行默认先调用父类无参数构造方法一次。
-
子类构造方法的第一行都隐含了一个super()去调用父类无参数构造方法,super()可以省略不写。
继承的特点:1.Java只支持单继承,不支持多继承。2.一个类可以有多个子类。3.可以多层继承。
多态
同类型的对象,表现出的不同形态。
父类类型 对象名称 = 子类对象;
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。
向上转型(自动转换):多态本身是子类类型向父类类型向上转换(自动转换)的过程,这个过程是默认的。 当父类引用指向一个子类对象时,便是向上转型。
使用格式:父类类型 变量名 = new 子类类型(); 如:Animal a = new Cat();
向下转型(强制转换):父类类型向子类类型向下转换的过程,这个过程是强制的。 一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
使用格式:子类类型 变量名 = (子类类型) 父类变量名; 如:Aniaml a = new Cat(); Cat c =(Cat) a;
instanceof关键字:
为了避免ClassCastException(类型转换异常 )的发生,Java提供了 instanceof
关键字,给引用变量做类型的校验,格式如下:
变量名 instanceof 数据类型
如果变量属于该数据类型或者其子类类型,返回true。
如果变量不属于该数据类型或者其子类类型,返回false。
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}
JDK14的时候提出了新特性,把判断和强转合并成了一行
//新特性
//先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d
//如果不是,则不强转,结果直接是false
if(a instanceof Dog d){
d.lookHome();
}else if(a instanceof Cat c){
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
包
包在操作系统中其实就是一个文件夹。包是用来分门别类的管理技术,不同的技术类放在不同的包下,方便管理和维护。
命名规范:路径名.路径名.xxx.xxx // 例如:com.itheima.oa
-
包名一般是公司域名的倒写。例如:黑马是www.itheima.com,包名就可以定义成com.itheima.技术名称。
-
包名必须用”.“连接。
-
包名的每个路径名必须是一个合法的标识符,而且不能是Java的关键字。
导包:
什么时候需要导包?
情况一:在使用Java中提供的非核心包中的类时
情况二:使用自己写的其他包中的类时
什么时候不需要导包?
情况一:在使用Java核心包(java.lang)中的类时
情况二:在使用自己写的同一个包中的类时
使用不同包下的相同类时,就需要使用全类名了。
//使用全类名的形式即可。
//全类名:包名 + 类名
//拷贝全类名的快捷键:选中类名crtl + shift + alt + c 或者用鼠标点copy,再点击copy Reference
com.itheima.homework.demo1.Student s1 = new com.itheima.homework.demo1.Student();
com.itheima.homework.demo2.Student s2 = new com.itheima.homework.demo2.Student();
权限修饰符
-
public:公共的,所有地方都可以访问。
-
protected:本类 ,本包,其他包中的子类都可以访问。
-
默认(没有修饰符):本类 ,本包可以访问。
注意:默认是空着不写,不是default
-
private:私有的,当前类可以访问。
public > protected > 默认 > private
public | protected | 默认 | private | |
同一类中 | √ | √ | √ | √ |
同一包中的类 | √ | √ | √ | |
不同包的子类 | √ | √ | ||
不同包中的无关类 | √ |
可见,public具有最大权限。private则是最小权限。
编写代码时,如果没有特殊的考虑,建议这样使用权限:
-
成员变量使用private,隐藏细节。
-
构造方法使用public ,方便创建对象。
-
成员方法使用public ,方便调用方法。
final关键字
Java提供了`final` 关键字,表示修饰的内容不可变。
final:不可改变,最终的含义。可以用于修饰类、方法和变量。
-
类:被修饰的类,不能被继承。
-
方法:被修饰的方法,不能被重写。
-
变量:被修饰的变量,有且仅能被赋值一次。
被final修饰的常量名称,一般都有书写规范,所有字母都大写。
抽象类
我们把没有方法体的方法称为抽象方法。Java语法规定,包含抽象方法的类就是抽象类。
abstract是抽象的意思,用于修饰方法和类。
抽象方法:使用abstract关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
抽象类:如果一个类包含抽象方法,那么该类必须是抽象类。注意:抽象类不一定有抽象方法,但是有抽象方法的类必须定义成抽象类。
继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。
接口
接口是更加彻底的抽象,JDK7之前,包括JDK7,接口中的只有包含:抽象方法和常量。接口同样是不能创建对象的。
定义格式
//接口的定义格式:
interface 接口名称{
// 抽象方法
}
// 接口的声明:interface
// 接口名称:首字母大写,满足“驼峰模式”
接口中的抽象方法默认会自动加上public abstract修饰,程序员无需自己手写。
接口的实现:
类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用implements关键字。
实现接口的格式:(可以被多实现)
/**接口的实现:
在Java中接口是被实现的,实现接口的类称为实现类。
实现类的格式:*/
class 类名 implements 接口1,接口2,接口3...{
}
1.必须重写实现的全部接口中所有抽象方法。
2.如果一个类实现了接口,但是没有重写完全部接口的全部抽象方法,这个类也必须定义成抽象类。
3.意义:接口体现的是一种规范,接口对实现类是一种强制性的约束,要么全部完成接口申明的功能,要么自己也定义成抽象类。这正是一种强制性的规范。
接口与接口的多继承:一个接口可以同时继承多个接口。
注意:类与接口是实现关系。接口与接口是继承关系。
接口继承接口就是把其他接口的抽象方法与本接口进行了合并。
内部类
将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。可以把内部类理解成寄生,外部类理解成宿主。
分类:
成员内部类,类定义在了成员位置 (类中方法外称为成员位置,无static修饰的内部类)
宿主:外部类对象。
使用格式:外部类.内部类
获取内部类对象的两种方式:
方式一:外部直接创建成员内部类的对象
外部类.内部类 变量 = new 外部类().new 内部类();
方式二:在外部类中定义一个方法提供内部类的对象
public class Test {
public static void main(String[] args) {
Outer.inner oi = new Outer().new inner();
oi.method();
}
}
class Outer { // 外部类
private int a = 30;
// 在成员位置定义一个类
class inner {
private int a = 20;
public void method() {
int a = 10;
System.out.println(???); // 10 答案:a
System.out.println(???); // 20 答案:this.a
System.out.println(???); // 30 答案:Outer.this.a
}
}
}
静态内部类,类定义在了成员位置 (类中方法外称为成员位置,有static修饰的内部类)
1.静态内部类可以直接访问外部类的静态成员。
2.静态内部类不可以直接访问外部类的非静态成员,如果要访问需要创建外部类的对象。
3.静态内部类中没有银行的Outer.this。
使用格式:外部类.内部类
创建格式:外部类.内部类 变量 = new 外部类.内部类构造器
局部内部类,类定义在方法内
class 外部类名 {
数据类型 变量名;
修饰符 返回值类型 方法名(参数列表) {
// …
class 内部类 {
// 成员变量
// 成员方法
}
}
}
匿名内部类,没有名字的内部类,可以在方法中,也可以在类中方法外。
//匿名内部类格式
new 父类名或者接口名(){
// 方法重写
@Override
public void method() {
// 执行语句
}
};
//使用方式
interface Swim {
public abstract void swimming();
}
public class Demo07 {
public static void main(String[] args) {
// 使用匿名内部类
new Swim() {
@Override
public void swimming() {
System.out.println("自由泳...");
}
}.swimming();
// 接口 变量 = new 实现类(); // 多态,走子类的重写方法
Swim s2 = new Swim() {
@Override
public void swimming() {
System.out.println("蛙泳...");
}
};
s2.swimming();
}
}
实际上,如果我们希望定义一个只要使用一次的类,就可考虑使用匿名内部类。匿名内部类的本质作用是为了简化代码。
GUI图形化界面
AWT包
Swing包
集合进阶
数组和集合的区别
相同点:都是容器,可以存储多个数据
不同点:数组的长度是不可变的,集合的长度是可变的;
数组可以存基本数据类型和引用数据类型;
集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类。
List系列集合:添加的元素是有序、可重复、有索引
Set系列集合:添加的元素是无序、不重复、无索引
Collection集合:是单列集合的顶层接口。
常见的使用方法:
boolean add(E e); //添加元素
boolean remove(Object o); //从集合中移除指定的元素
boolean removeIf(Object o); //根据条件进行移除
void clear(); //清空集合中的元素
boolean contains(Object o); //判断集合中是否存在指定的元素
boolean isEmpty(); //判断集合是否为空
int size(); //集合的长度,也就是集合中元素的个数
Collection集合的遍历:
(1)迭代器遍历
Iterator<E> iterator(); //返回迭代器对象,默认指向当前对象的0索引
boolean hasNext(); //判断当前位置是否有元素可以被取出
E next(); //获取当前位置的元素,将迭代器对象移向下一个索引位置
eg:
ArrayList<String> list = new ArrayList<>();
Iterator<String> it = list.iterator();
while(it.hasNext()){
String s = it.next();
if("b".equals(s)){//指向谁,那么此时就删除谁.
it.remove();
}
迭代器遍历完毕,指针不会复位;循环中只能使用一次next方法;迭代器遍历时,不能使用集合的方法进行增加或删除。
(2)增强for遍历(所有的单列集合和数组)
-
它是JDK5之后出现的,其内部原理是一个Iterator迭代器
-
实现Iterable接口的类才可以使用迭代器和增强for
-
简化数组和Collection集合的遍历
for(集合/数组中元素的数据类型 变量名 : 集合/数组名) {
// 已经将当前遍历到的元素封装到变量中了,直接使用变量即可
}
eg:
ArrayList<String> list = new ArrayList<>();
for(string s : list){
System.out.println(s);
}
修改增强for中的变量,不会改变集合中原本的数据。
(3)Lambda表达式
利用forEach方法
public class A07_CollectionDemo7 {
public static void main(String[] args) {
/*
lambda表达式遍历:
default void forEach(Consumer<? super T> action):
*/
//1.创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("zhangsan");
coll.add("lisi");
coll.add("wangwu");
//2.利用匿名内部类的形式
//底层原理:
//其实也会自己遍历集合,依次得到每一个元素
//把得到的每一个元素,传递给下面的accept方法
//s依次表示集合中的每一个数据
/* coll.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});*/
//lambda表达式
coll.forEach(s -> System.out.println(s));
}
}
异常
指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理。
异常作用:(1)用来查询bug的关键参考信息
(2)可以作为方法内部的一种特殊返回值,以便通知调用者底层执行情况
Throwable体系:Error:严重错误,绝症,无法处理;
Exception:异常,可以通过代码纠正。
Throwable常用方法:
public void printStackTrace():控制台打印异常的详细信息。
包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。
public String getMessage():获取发生异常的原因。
public String toString():获取异常的类型和异常描述信息(不用)。
异常处理方式:
(1)JVM虚拟机默认处理方式:把异常的名称,原因,出现位置打印在控制台;程序停止运行,异常下面的代码不会再执行了。
(2)自己处理异常(捕获异常):try...catch...
try{
编写可能会出现异常的代码
}catch(异常类型 e){
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}
eg:
public class TryCatchDemo {
public static void main(String[] args) {
try {// 当产生异常时,必须有处理方式。要么捕获,要么声明。
read("b.txt");
} catch (FileNotFoundException e) {// 括号中需要定义什么呢?
//try中抛出的是什么异常,在括号中就定义什么异常类型
System.out.println(e);
}
System.out.println("over");
}
/*
*
* 我们 当前的这个方法中 有异常 有编译期异常
*/
public static void read(String path) throws FileNotFoundException {
if (!path.equals("a.txt")) {//如果不是 a.txt这个文件
// 我假设 如果不是 a.txt 认为 该文件不存在 是一个错误 也就是异常 throw
throw new FileNotFoundException("文件不存在");
}
}
}
finally代码块: 有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的。
try...catch....finally:自身需要处理异常,最终还得关闭资源。比如IO流中,当打开了一个关联文件的资源,最后程序不管结果如何,都需要把这个资源关闭掉。
当只有在try或者catch中调用退出JVM的相关方法,此时finally才不会执行,否则finally永远会执行。
(3) 抛出处理
throws 写在方法定义处,表示声明一个异常,告诉调用者使用本方法可能会有哪些异常。
public void 方法()throws 异常类名1,,异常类名2...{...}
编译时异常必须要写!运行时异常可以不写。
throw 写在方法内,用来手动抛出一个指定的异常对象,交给调用者,并结束当前方法。
public void 方法(){
throw new NullPointerException();
}
throw new 异常类名(参数);
IO流
读写文件中的数据。(以程序(内存)为参照物)
根据数据的流向分类:
input输入流:把数据从其他设备上读取到内存中的流。
output输出流:把数据从`内存` 中写出到`其他设备`上的流。
根据数据的类型分类:
字节流:以字节为单位,读写数据的流。
字符流:以字符为单位,读写数据的流。
顶级父类:
输入流 | 输出流 | |
字节流 | InputStream | OutputStream |
字符流 | Reader | Writer |
字节流:
字节输出流:
三种输出方式:
public class Test1 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("D:\\aaa.txt");
fos.write(97);
fos.close();
}
}
public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
// 字符串转换为字节数组
byte[] b = "黑马程序员".getBytes();
// 写出字节数组数据
fos.write(b);
// 关闭资源
fos.close();
}
}
输出结果:
黑马程序员
public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
// 字符串转换为字节数组
byte[] b = "abcde".getBytes();
// 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd。
fos.write(b,2,2);
// 关闭资源
fos.close();
}
}
输出结果:
cd
回车换行:
String wrap = "\r\n";
byte[] byte1 = wrap.getBytes();
fos.write(byte1);
fos.write("\r\n".getBytes());
续写:在文件后面加上true
FileOutputStream fos = new FileOutputStream("fos.txt",true);
字节输入流:
构造举例:
// 使用File对象创建流对象
File file = new File("a.txt");
FileInputStream fos = new FileInputStream(file);
// 使用文件名称创建流对象
FileInputStream fos = new FileInputStream("b.txt");
读取字节数据:
(1)read方法,每次读取一个字节,提升为int类型,读取到文件末尾返回-1;
public class FISRead {
public static void main(String[] args) throws IOException{
// 使用文件名称创建流对象
FileInputStream fis = new FileInputStream("read.txt");
// 读取数据,返回一个字节
int read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
// 读取到末尾,返回-1
read = fis.read();
System.out.println( read);
// 关闭资源
fis.close();
}
}
(2)使用字节数组读取:read(byte[] b)每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回`-1`
public class FISRead {
public static void main(String[] args) throws IOException{
// 使用文件名称创建流对象.
FileInputStream fis = new FileInputStream("read.txt"); // 文件中为abcde
// 定义变量,作为有效个数
int len ;
// 定义字节数组,作为装字节数据的容器
byte[] b = new byte[2];
// 循环读取
while (( len= fis.read(b))!=-1) {
// 每次读取后,把数组变成字符串打印
System.out.println(new String(b,0,len));// len 每次读取的有效字节个数
}
// 关闭资源
fis.close();
}
}
流的关闭原则:先开后关,后开先关。
文件拷贝
FileInputStream fis = new FileInputStream("D:\\movie.mp4");
FileOutputStream fos = new FileOutputStream("myio\\copy.mp4");
int b;
while((b = fis.read()) != -1){
fos.write(b);
}
fos.close();
fis.close();
int len;
byte[] bytes = new byte[1024 * 1024 * 5];// 5M大小的字节数组
while((len = fis.read(bytes)) != -1){
fos.write(bytes,off,0,len);
}
字符流:
字符输入流Reader:
public class FRRead {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileReader fr = new FileReader("read.txt");
// 定义变量,保存数据
int b ;
// 循环读取
while ((b = fr.read())!=-1) {
System.out.println((char)b);
}
// 关闭资源
fr.close();
}
}
public class FRRead {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileReader fr = new FileReader("read.txt");
// 定义变量,保存有效字符个数
int len ;
// 定义字符数组,作为装字符数据的容器
char[] cbuf = new char[2];
// 循环读取
while ((len = fr.read(cbuf))!=-1) {
System.out.println(new String(cbuf,0,len));
}
// 关闭资源
fr.close();
}
}
字符输出流Writer:
public class FWWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileWriter fw = new FileWriter("fw.txt");
// 写出数据
fw.write(97); // 写出第1个字符
fw.write('b'); // 写出第2个字符
fw.write('C'); // 写出第3个字符
fw.write(30000); // 写出第4个字符,中文编码表中30000对应一个汉字。
/*
【注意】关闭资源时,与FileOutputStream不同。
如果不关闭,数据只是保存到缓冲区,并未保存到文件。
*/
// fw.close();
}
}
多线程
进程:程序的基本执行实体。
线程:操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
并发:同一时刻,多个指令在单个CPU上交替执行。
并行:同一时刻,多个指令在多个CPU上同时执行。
实现方式:
(1)继承Thread类
public class MyThread extends Thread {
@Override
public void run() {
for(int i=0; i<100; i++) {
System.out.println(getName() + "HelloWorld");
}
}
}
public class MyThreadDemo {
public static void main(String[] args) {
MyThread my1 = new MyThread();
MyThread my2 = new MyThread();
t1.setName("线程1");
t2.setName("线程2");
// my1.run();
// my2.run();
//void start() 导致此线程开始执行; Java虚拟机调用此线程的run方法
my1.start();
my2.start();
}
}
/*
1.为什么要重写run()方法?
因为run()是用来封装被线程执行的代码
2.run()方法和start()方法的区别?
run():封装线程执行的代码,直接调用,相当于普通方法的调用
start():启动线程;然后由JVM调用此线程的run()方法
*/
(2)实现Runnable接口
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int i=0; i<100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class MyRunnableDemo {
public static void main(String[] args) {
//创建MyRunnable类的对象
MyRunnable my = new MyRunnable();
//创建Thread类的对象,把MyRunnable对象作为构造方法的参数
//Thread(Runnable target)
// Thread t1 = new Thread(my);
// Thread t2 = new Thread(my);
//Thread(Runnable target, String name)
Thread t1 = new Thread(my,"坦克");
Thread t2 = new Thread(my,"飞机");
//启动线程
t1.start();
t2.start();
}
}