Java
cmd命令
盘的名称+冒号:进入该盘
dir:显示该路径下所有文件
cd+路径:进入某一目录
cd...:退到上一级
打开程序:进入exe文件的目录,直接输入exe可以启动
用cmd编译并运行Java
C:\Users\张瑞枝>D:
//进入盘
D:\>cd eclipse-workspace\Homework\src
//进入Java文件所在目录
D:\eclipse-workspace\Homework\src>javac Helloworld.java
//用Javac编译Java文件
D:\eclipse-workspace\Homework\src>java Helloworld
Helloworld!
//用Java运行文件
'\t' 制表符 将前面字符串的长度变成8或者8的倍数,便于对齐
标识符:起名字,可由数字、字母、下划线(_)和美元符($)组成
Java结构:project - module - package - class
二进制:0b开头
十进制:不加开头
八进制:0开头
十六进制:0x开头
byte:1个字节
short:2个字节
int:4个字节
long:8个字节
java
long n = 9999999999L;
float f = 10.1F;
//上面两个赋值时需要在后面加相应字母,最好大写
boolean b = true;
//Java中布尔类型只有false和true,没有0和1
java
//键盘录入
//快速生成第三行:psvm
//快速生成打印:sout
import java.util.Scanner;//导包
public class ScannerDemo {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);//创建对象
int i=sc.nextInt();//接收数据
int k=sc.nextInt();
System.out.println(i);
System.out.println(k);
}
}
数据类型
基本数据类型:整数类型、浮点数类型、布尔类型、字符类型
直接存储数据,存在栈内存中
赋值给其他变量,也是赋的真实的值
引用数据类型:除了左边的其他所有类型
存储的是数据在其他空间的地址值
赋值给其他变量,赋的是地址值
参数传递
如果直接传基本数据类型,则两个方法之间不会相互影响
如果传的是引用数据类型,传递的是地址,则会改变地址所在处的值
运算符
运算符的类型转换
隐式转换
取值范围小的数值 --> 取值范围大的数值
byte --> short --> int --> long --> float --> double
byte、short、char三种类型的数据在运算的时候,都会直接先提升为int,然后再进行运算
强制转换
java
public class Main {
public static void main(String[] args) {
int i = 8;
double k =(double)(i * i);
System.out.println(k);
}
}
64.0
运算符
++和--在同一行时放在前面和后面都一样
否则b=a++是先赋值后自增,b=++a是先自增后赋值
+= 、-= 、*= 、/= 、%=都隐藏着强制类型转换
且:& 两边都要判断
或:|
左移:<< 向左移动,低位补0
右移:>> 向右移动,高位补0或1(正数补0,负数补1)
无符号右移:>>>(向右移动,高位补0)
异或:^ 相同为false,不同为true
上面是对二进制补码进行操作
非:!
短路逻辑运算符
而且:&& 前面的判断为假就不判断下一个
或:|| 前面的判断为假就不判断下一个
打印
java
//把ln删掉时没有换行
public class practice1 {
public static void main(String[] args) {
int[] a = {1,2,3,4,5};
int b = a[0];
a[0] = a[4];
a[4] = b;
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + ",");
}
//下面这个用法和c++的printf相似
System.out.printf("你好%d",5);
}
}
5,2,3,4,1,你好5
判断与循环及键盘录入
java
//":"可以换成"->",不过要版本高,就可以省略break
public class Main {
public static void main(String[] args) {
int number = 1;
switch(number){
case 1 :{
System.out.println("one");
break;
}
case 2 :{
System.out.println("two");
break;
}
case 3 :{
System.out.println("three");
break;
}
default :{
System.out.println("none");
break;
}
}
}
}
java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
//录入整数
//录入字符串为next()或nextLine()
int day = sc.nextInt();
switch(day){
case 1: case 2: case 3: case 4:case 5:
System.out.println("工作日");
break;
case 6:case 7:
System.out.println("休息日");
break;
default :{
System.out.println("none");
}
}
}
}
//键盘录入
//第一套体系:nextInt(),nextDouble(),next()(接受字符串,遇到空格、制表符、回车就不接收了)
//第二套体系:nextLine(),接受字符串,遇到空格和制表符仍然可以接受,遇到回车不接收
//如果用了体系一的再用体系二,可能会导致nextLine()接收不到
数组
每个方法依次放入栈内存中,main方法放在最下面
每new一个就在堆里开辟一个空间,存储数组中的数据,故数组本身是地址值
java
//静态数组初始化
//两种写法
public class array {
public static void main(String[] args) {
int[] array1 = new int[]{1,2,3};
int[] array2 = {1,2,3};
String[] array3 = new String[]{"aa","bb","cc"};
String[] array4 = {"aa","bb","cc"};
double[] array5 = new double[]{1.1,2.2,3.3};
double[] array6 = {1.1,2.2,3.3};
System.out.println(array1);
System.out.println(array5);
}
}
[I@4554617c
[D@1540e19d
//[:表示当前是一个数组
//D:表示当前数组的元素都是double类型的
//I:表示当前数组的元素都是int类型的
//@:表示一个间隔符号(固定格式)
//4554617c和1540e19d才是真正的地址值(十六进制)
java
//数组遍历
//快速生成for那一行:arr.fori
public class array {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
java
//动态初始化数组
public class array {
public static void main(String[] args) {
String[] s = new String[50];
s[0] = "haha";
s[1] = "yes";
System.out.println(s[0]);
System.out.println(s[1]);
System.out.println(s[2]);
int[] a = new int[5];
System.out.println(a[0]);
System.out.println(a[1]);
}
}
haha
yes
null
0
0
//数组默认初始化规律
//整数类型:0
//小数类型:0.0
//字符类型:空格字符
//布尔类型:false
//引用数据类型:null
java
//二维数组的初始化
public class DoubleArray {
public static void main(String[] args) {
int[][] arr1 = new int[][]{{1,2},{3,4}};
int[][] arr2 = {
{1,2},
{3,4}
};
int[][] arr3 = new int[3][4];
System.out.println(arr1[0]);
System.out.println(arr1[0][0]);
}
}
[I@4554617c
1
java
//二维数组中每一项是一维数组
public class DoubleArray {
public static void main(String[] args) {
int[][] arr3 = new int[2][];
int[] arr1 = {1,2,3,4,5};
int[] arr2 = {6,7,8};
//赋值的改变是直接改变地址
arr3[0] = arr1;
arr3[1] = arr2;
for (int i = 0; i < arr3.length; i++) {
for (int j = 0; j < arr3[i].length; j++) {
System.out.print(arr3[i][j]);
}
}
}
}
12345678
随机数
java
public class array {
public static void main(String[] args) {
int[] a = new int[10];
Random r = new Random();
for (int i = 0; i < a.length; i++) {
//r.nextInt(100)生成0到99的随机数,加一则得到1到100随机数
int num = r.nextInt(100) + 1;
a[i] = num;
}
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
}
}
方法的重载
-
在同一个类中,定义了多个同名的方法,这些同名的方法具有同种的功能
-
每个方法具有不同的参数类型或参数个数,这些同名的方法,就构成了重载关系
-
简单记:同一个类中,方法名相同,参数不同的方法,与返回值无关。
参数不同:个数不同、类型不同、顺序不同
面向对象
一些规范:类名首字母大写每个单词第一个字母大写,方法名及其他名首字母小写,第二个单词开始每个单词第一个字母大写。
-
用来描述一类事物的类,叫做Javabean类
在Javabean类中,是不写main方法的
-
编写main方法的类,叫做测试类
我们可以在测试类中创建Javabean类的对象并进行赋值调用
java
//创建类
public class Phone {
//成员变量
String brand;
double price;
//成员方法
public void call(){
System.out.println("手机在打电话");
}
public void playGame(){
System.out.println("手机在玩游戏");
}
}
//创建对象并赋值调用
public class PhoneTest {
public static void main(String[] args) {
Phone p = new Phone();
p.brand = "华为";
p.price = 3999.9;
System.out.println(p.brand);
System.out.println(p.price);
p.call();
p.playGame();
}
}
华为
3999.9
手机在打电话
手机在玩游戏
封装
对象有什么属性,就封装其属性对应的行为。
private
- 是一个权限修饰符
- 可以修饰成员(成员变量和成员方法)
- 被private修饰的成员只能在本类中才能访问
- 目的是防止值被随意错误更改
java
//创建类
//提供setXxx方法,用于给成员变量赋值,方法用public修饰
//提供getXxx方法,用于获取成员变量的值,方法用public修饰
public class BoyFriend {
private String name;
private int age;
public void setName(String n){
name = n;
}
public String getName(){
return name;
}
public void setAge(int a){
if(a>=18 && a<=30) age = a;
else System.out.println("非法参数");
}
public int getAge(){
return age;
}
public void sing(){
System.out.println("男朋友在唱歌");
}
public void dance(){
System.out.println("男朋友在跳舞");
}
}
//创建对象并赋值调用
//这里打bf.name或bf.age会出错
public class BoyFriendTest {
public static void main(String[] args) {
BoyFriend bf = new BoyFriend();
bf.setAge(40);
bf.setName("薛之谦");
System.out.println(bf.getName());
System.out.println(bf.getAge());
bf.sing();
bf.dance();
}
}
this
java
//创建类
public class Phone {
private String brand;
private double price;
public void method(){
double price = 10.0;
//就近原则,所以下面的price是局部变量的price
System.out.println(price);
//this则表示成员位置的值
System.out.println(this.price);
}
}
//创建对象,赋值并调用方法
public class PhoneTest {
public static void main(String[] args) {
Phone p = new Phone();
p.method();
}
}
10.0
0.0
java
//创建类,使用this
public class BoyFriend {
private String name;
private int age;
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
if(age>=18 && age<=30) this.age = age;
else System.out.println("非法参数");
}
public int getAge(){
return age;
}
public void sing(){
System.out.println("男朋友在唱歌");
}
public void dance(){
System.out.println("男朋友在跳舞");
}
}
//创建对象,赋值并调用方法
public class BoyFriendTest {
public static void main(String[] args) {
BoyFriend bf = new BoyFriend();
bf.setAge(40);
bf.setName("薛之谦");
System.out.println(bf.getName());
System.out.println(bf.getAge());
bf.sing();
bf.dance();
}
}
非法参数
薛之谦
0
男朋友在唱歌
男朋友在跳舞
构造方法
- 方法名与类名相同,大小写也相同
- 没有返回值类型,void也没有
- 没有具体的返回值,不能用return返回结果数据
- 每创建一次对象,虚拟机就会调用一次构造方法,不能手动调用构造方法
如果定义了构造方法,则系统不再提供默认构造方法。
无论是否使用,都手动书写无参数构造方法,和带全部参数的构造方法。
java
//创建类
public class Student {
private String name;
private int age;
//默认的构造方法
//这里也体现了构造方法的重载
//快捷键:alt+insert,选择Constructor
//另外可以用插件,右键选择Ptg To JavaBean快速构造JavaBean
public Student(){
}
//自己定义的构造方法,最好两种构造方法都要写
public Student(String name,int age){
this.name = name;
this.age = age;
}
//快捷键:alt+insert,选择Getter and Setter
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
}
//创建对象,赋值并调用方法
public class StudentTest {
public static void main(String[] args) {
Student s = new Student();
//可以在创建对象时直接赋值
Student e = new Student("薛之谦",40);
System.out.println(s.getName());
System.out.println(s.getAge());
System.out.println(e.getName());
System.out.println(e.getAge());
}
}
null
0
薛之谦
40
JavaBean
标准的JavaBean类
- 类名见名知意
- 所有成员变量使用private修饰
- 提供至少两个构造方法:无参构造方法和带全部参数的构造方法
- 提供每个成员变量的setXxx和getXxx
内存
栈内存:存储方法,先进后出
堆内存:存储new的对象
方法区:存储类
对象存储的是在堆内存的地址,成员方法存储的是在方法区的地址
下面这个新建两个对象时,class文件不需要再次加载在方法区了,可直接使用
下面是两个引用指向同一个对象,则是地址值的赋值
this的内存原理
this的本质:代表方法调用者的地址
s存储的是在堆内存中的地址,调用method方法后,this表示的就是s存储的地址
setName中this的原理也是如此
字符串
构造方法
字符串是java定义好的一个类,有一些构造方法
java
public class s1 {
public static void main(String[] args) {
String s1 = "abc";
System.out.println(s1);
//空参构造
String s2 = new String();
System.out.println(s2);
//传递字符串构造
String s3 = new String("abc");
System.out.println(s3);
//传递字符数组构造
char[] c = {'a','b','c','d'};
String s4 = new String(c);
System.out.println(s4);
//传递字节数组,根据字节数组对应在ASCII表中字符创建一个字符串
byte[] b ={97,98,99,100};
String s5 = new String(b);
System.out.println(s5);
}
}
abc
abc
abcd
abcd
字符串的比较
java
//基本数据类型比较的是数据值,引用数据类型比较的是地址值
public class s1 {
public static void main(String[] args) {
//两个字符串都是存储在串池中,结果为相等
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);
//每new一次会在堆中开辟一个新的地址,故两个的地址不同,结果为不相等
String s3 = new String("abc");
String s4 = new String("abc");
System.out.println(s1 == s3);
System.out.println(s1 == s3);
//键盘录入的也会new一个String,故地址不同
Scanner sc = new Scanner(System.in);
String s5 = sc.next();
System.out.println(s1 == s5);
}
}
true
false
false
abc
false
java
public class s1 {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "abc";
String s3 = "ABc";
//equals()和equalsIgnoreCase()可以用来比较字符串的内容,返回的是布尔类型
//equals()方法完全相同才是true
//erualsIgnoreCase()方法不比较大小写
boolean result1 = s1.equals(s2);
boolean result2 = s1.equals(s3);
boolean result3 = s1.equalsIgnoreCase(s3);
System.out.println(result1);
System.out.println(result2);
System.out.println(result3);
}
}
true
false
true
字符串索引
java
public class ForString {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.next();
//快捷键:s.length().fori
//s.length().forr倒着遍历
for(int i = 0;i< s.length();i++){
//charAt()方法,用来索引
System.out.print(s.charAt(i));
}
}
}
hahaxzqloveyou
hahaxzqloveyou
public class ForString {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.next();
int upper = 0, lower = 0, number = 0;
for (int i = 0; i < s.length(); i++) {
if(s.charAt(i) >= 'a' && s.charAt(i) <= 'z') lower ++;
else if(s.charAt(i) >= 'A' && s.charAt(i) <= 'Z') upper ++;
else if(s.charAt(i) >= '0' && s.charAt(i) <= '9') number ++;
}
System.out.println(upper + ", " + lower + ", " + number);
}
}
loveXZQ1314
3, 4, 4
substring
java
public class Substring {
public static void main(String[] args) {
String s1 = "anbcgak";
//包前不包后,都是索引
String s2 = s1.substring(2,5);
//下面这个表示索引3一直截取到字符串最后
String s3 = s1.substring(3);
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
}
}
anbcgak
bcg
cgak
replace
java
public class StringDemo1 {
public static void main(String[] args) {
String num = "lovexzqmd";
String s = new String();
//参数:被替代的字符串,替代的字符串
s = num.replace("md","1314");
System.out.println(s);
}
}
lovexzq1314
StringBuilder
一个类,StringBuilder可以看成是一个容器,创建之后里面的内容是可变的。
作用是提高字符串的操作效率。
java
//两个构造方法
//空参构造
public StringBuilder()
//根据字符串的内容,创建可变字符的对象
public StringBuilder(String s)
常用方法
java
public class StringDemo2 {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
//打印出来的不是地址值而是属性值
//因为这是Java已经写好的类,Java在底层对它做了特殊处理
System.out.println(sb);
//可以直接对类中对象修改
//append()向字符串添加内容
sb.append(1);
sb.append(2.3);
sb.append(true);
System.out.println(sb);
//可以使用链式编程,如下
//sb.append(1).append(2.3).append(true)
//reverse反转字符串
sb.reverse();
System.out.println(sb);
//toString方法把它变成字符串类型
String s = sb.toString();
System.out.println(s);
}
}
12.3true
eurt3.21
eurt3.21
StringJoiner
和StringBuilder一样,可以看成是一个容器,创建后里面的内容是可变的。
作用是提高字符串的操作效率。
java
//构造方法 没有空参构造
public StringJoiner(间隔符号)
public StringJoiner(间隔符号,开始符号,结束符号)
java
public class StringDemo5 {
public static void main(String[] args) {
StringJoiner sj = new StringJoiner("---");
//add方法
sj.add("aaa").add("bbb").add("ccc");
System.out.println(sj.toString());
StringJoiner s = new StringJoiner(",","[","]");
s.add("aaa").add("bbb").add("ccc");
System.out.println(s);
System.out.println(s.length());
}
}
aaa---bbb---ccc
[aaa,bbb,ccc]
13
字符串拼接的底层原理
情况一:拼接的时候没有变量参与
会复用串池中的字符串,直接赋值也会复用串池中的字符串
情况二:拼接的时候有变量参与
会创建StringBuilder对象
JDK7及以前的创建方法
JDK8的创建方法
先预估字符串长度,然后创建数组,将对象放进去,再转化为字符串
字符串拼接时最好用StringBuilder或者StringJoiner
StringBuilder源码分析
- 默认创建一个容量为16的的字节数组
- 添加的内容长度小于等于16,直接存
- 添加的内容长度大于16会扩容(原来的容量*2+2)
- 如果添加的内容长度大于扩容之后的长度,则直接创建添加的内容长度的数组
java
//注意这里是初始添加的情况
public class StringDemo6 {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
//capacity()返回容量
System.out.println(sb.capacity());
System.out.println(sb.length());
sb.append("qwertyuiopasdfghjklzxcvbnm");
System.out.println(sb.capacity());
System.out.println(sb.length());
StringBuilder sb2 = new StringBuilder();
sb2.append("qwertyuiopasdfghjklzxcvbnm1234567890");
System.out.println(sb2.capacity());
System.out.println(sb2.length());
}
}
16
0
34
26
36
26
toCharArray
java
public class StringDemo7 {
public static void main(String[] args) {
String s = "abcde";
char[] c = s.toCharArray();
//得到的数组是['a','b','c','d','e']
}
}
包装类
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
static
静态方法中没有this关键字
静态方法中,只能调用静态
非静态方法可以访问所有
被static修饰的成员变量,叫做静态变量。
- 被该类所有对象共享
- 不属于对象,属于类
- 随着类的加载而加载,优先于对象存在
被static修饰的成员变量,叫做静态方法。
- 多用在测试类和工具类中
- Javabean类中很少会用
java
//创建类
public class Student {
private String name;
private int age;
private String gender;
//创建一个static的属性,则该类共享这一属性的值
public static String teacher;
public Student() {
}
public Student(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
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;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
public class StudentTest {
public static void main(String[] args) {
Student s1 = new Student("xzq",40,"男");
Student s2 = new Student("zrz",19,"女");
//直接对类进行赋值
Student.teacher = "阿玮老师";
System.out.println(s1.getName() + ", " + s1.getGender() + ", " + s1.getAge() + ", " + s1.teacher);//xzq, 男, 40, 阿玮老师
System.out.println(s2.getName() + ", " + s2.getGender() + ", " + s2.getAge() + ", " + s2.teacher);//zrz, 女, 19, 阿玮老师
}
}
static内存图
静态区在堆内存中
工具类
帮我们做一些事情,不描述任何事物的类。
java
//类名见名知意
public class ArrUtil {
//私有化构造方法
private ArrUtil(){}
//方法定义为静态
public static int getMax(){}
public static int getMin(){}
public static int getSum(){}
public static int getAvg(){}
}
继承
当类与类之间,存在共性的内容,并满足子类是父类中的一种,就可以考虑使用继承来优化代码。
继承可以把多个子类中重复的代码抽取到父类中,子类可以直接使用,减少代码冗余,提高代码的复用性。
java
//继承格式
//子类可以使用父类的属性和行为
//子类可以在父类的基础上新增其他功能,子类更强大
public class 子类 extends 父类 {}
Java支持单继承,不支持多继承,支持多层继承。
单继承:一个子类只能继承一个父类
多继承:一个子类可以继承多个父类
多层继承:子类A继承父类B,父类B继承父类C
每个类都直接或间接继承于Object类
画图法:从子类到父类画,抽取共性
写代码:从父类到子类
子类只能访问父类中非私有的成员。
java
//构造方法私有还是非私有都不能被继承
//成员变量私有还是非私有都可以被继承,私有的可以被继承,不能直接使用
//成员方法可以被添加到虚方法表的可以被继承,否则不可以被继承
继承的内存图如下
A继承C时不是一层一层往上找的
只有父类中的虚方法才能被子类继承
虚方法如下图
java
//成员变量的访问:就近原则
//先在本类找,找不到再去父类找
public class Fuzi {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}
class Fu{
String name = "Fu";
}
class Zi extends Fu{
String name = "Zi";
public void show(){
String name = "ziShow";
//就近原则
System.out.println(name);//ziShow
//调用本类的
System.out.println(this.name);//Zi
//调用父类的
//最多只能调用一个直接父类的
System.out.println(super.name);//Fu
}
}
java
//继承中方法的调用:就近原则
public class Test {
public static void main(String[] args) {
OverseasStudent os = new OverseasStudent();
os.lunch();
}
}
class Person{
public void eat(){
System.out.println("eating");
}
public void drink(){
System.out.println("drinking");
}
}
class OverseasStudent extends Person{
public void lunch(){
//先在本类找,没有再去父类找
this.eat();
this.drink();
//直接去父类找
super.eat();
super.drink();
}
public void eat(){
System.out.println("eating other");
}
public void drink(){
System.out.println("drinking other");
}
}
class Students extends Person{
public void lunch(){
//先在本类找,再去父类找
eat();
drink();
}
}
eating
drinking
eating other
drinking other
eating
drinking
方法的重写
- 当父类的方法不能满足子类的需求时,需要进行方法重写
- @Override是放在重写后的方法上,校验子类重写时语法是否正确。
java
class Person{
public void eat(){
System.out.println("eating");
}
public void drink(){
System.out.println("drinking");
}
}
class OverseasStudent extends Person{
public void lunch(){
this.eat();
this.drink();
super.eat();
super.drink();
}
@Override
public void eat(){
System.out.println("eating other");
}
@Override
public void drink(){
System.out.println("drinking other");
}
}
方法重写的本质:覆盖父类给的虚方法表
java
public class DogTest {
public static void main(String[] args) {
Husky h = new Husky();
h.eat();
h.drink();
h.lookHome();
h.destory();
ChineseDog cd = new ChineseDog();
cd.eat();
cd.drink();
cd.lookHome();
}
}
public class Dog {
public void eat(){
System.out.println("吃狗粮");
}
public void drink(){
System.out.println("喝水");
}
public void lookHome(){
System.out.println("看家");
}
}
public class Husky extends Dog{
public void destory(){
System.out.println("拆家");
}
}
public class ShapiDog extends Dog{
//这里可以直接快捷写eat()
@Override
public void eat() {
//先调用父类的,再进行补充
super.eat();
System.out.println("吃骨头");
}
}
public class ChineseDog extends Dog{
//同样可以快捷键eat()
@Override
public void eat(){
System.out.println("吃剩饭");
}
}
继承中构造方法的访问
java
//子类中所有构造方法默认先访问父类中的无参构造,再执行自己
//因为子类初始化前,一定要调用父类构造方法完成父类数据空间的初始化
//子类构造方法第一句默认是super(),不写也存在,必须写在第一句
//如果要调用父类有参构造,必须手动写super进行调用
public class Person {
String name;
int age;
public Person() {
System.out.println("这里是父类");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Student extends Person{
public Student(){
//先调用父类的无参构造,再调用子类的
super();
System.out.println("这里是子类");
}
//直接传递参数,传到super里,就会去父类找到对应的有参构造
public Student(String name,int age){
super(name,age);
}
}
public class PersonTest {
public static void main(String[] args) {
Student s1 = new Student();
/*这里是父类
这里是子类*/
Student s2 = new Student("xzq",40);
System.out.println(s2.name + ", " + s2.age);//xzq, 40
}
}
java
public class Person {
String name;
int age;
public Person() {
System.out.println("这里是父类");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Student extends Person{
public Student(){
//表示调用本类其他构造方法,即下面的构造方法
//没传参时默认传的是调用者的this
this(null,18);
}
public Student(String name,int age){
super(name,age);
}
}
public class PersonTest {
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(s1.name + ", " + s1.age);
}
}
多态
多态即同类型的对象,表现出的不同形态。
java
//多态优势:方法中,使用父类型作为参数,可以接收所有子类对象
//多态弊端:不能使用子类特有的功能
//强制类型转换可以转换成真正的子类类型,从而调用子类独有功能
表现形式:父类类型 对象名称 = 子类对象;
多态的前提
- 有继承关系
- 有父类引用指向子类对象
- 有方法重写
java
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
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;
}
public void show(){
System.out.println(name + ", " + age);
}
}
public class Student extends Person{
@Override
public void show() {
System.out.println(this.getName() + ", " + this.getAge());
}
}
public class Teacher extends Person{
@Override
public void show() {
System.out.println(this.getName() + ", " + this.getAge());
}
}
public class Test {
public static void main(String[] args) {
Student s = new Student();
s.setName("xzq");
s.setAge(40);
Teacher t = new Teacher();
t.setName("zrz");
t.setAge(19);
show(s);
show(t);
}
//使用父类型作为参数,可以接收所有子类对象
//不过必须要有方法的重写
public static void show(Person p){
p.show();
}
}
java
public class AnimalTest {
public static void main(String[] args) {
Animal a = new Dog();
//访问成员属性时:编译看左边,运行看左边
//javac编译时,会看父类中有没有这个成员变量,有则编译成功,没有则编译失败
//java运行时,实际获取的是左边父类中成员变量的值
System.out.println(a.name);//动物
//访问成员方法时:编译看左边,运行看右边
//javac编译时,会看父类中有没有这个成员方法,有则编译成功,没有则编译失败
//java运行时,实际获取的是右边子类中的成员方法
a.show();//dog show
}
}
class Animal{
String name = "动物";
public void show(){
System.out.println("animal show");
}
}
class Dog extends Animal{
String name = "狗";
@Override
public void show() {
System.out.println("dog show");
}
}
java
public class AnimalTest {
public static void main(String[] args) {
Animal a = new Dog();
//instanceof用于判断前面那个对象是不是属于后面那个类
if(a instanceof Dog){
//若不强转,则用a无法调用子类方法中的eat()
Dog d = (Dog) a;
d.eat();//dog is eating
}else if(a instanceof Cat){
Cat c = (Cat) a;
c.eat();
}
//另一个简单写法,要新版本可用
//如果a是Dog类则强转且转后命名为d
if(a instanceof Dog d){
d.eat();
}else if(a instanceof Cat c){
c.eat();
}
}
}
class Animal{
String name = "动物";
public void show(){
System.out.println("animal show");
}
}
class Dog extends Animal{
String name = "狗";
@Override
public void show() {
System.out.println("dog show");
}
public void eat(){
System.out.println("dog is eating");
}
}
class Cat extends Animal{
@Override
public void show() {
System.out.println("cat show");
}
public void eat(){
System.out.println("cat is eating");
}
}
包
包就是文件夹,用来管理各种不同功能的Java类。
**包名的规则:公司域名反写 + 包的作用,需要全部英文小写,见名知意。**例:com.itheima.domain
一个类的全类名应为 包名.类名 例:com.itheima.domain.Student
- 使用同一个包中的类时,不需要导包
- 使用java.lang包中的类时,不需要导包
- 其他情况都要导包,例import com.itheima.domain.Student则默认用到的Student类都是导包中的这个类
- 如果同时使用两个包中的同名类,需要用全类名
final
- final修饰方法,表示该方法是最终方法,不能被子类重写
- final修饰类,表示该类是最终类,不能被继承
- final修饰变量,叫做常量,只能被赋值一次
常量命名规范
- 单个单词:全部大写
- 多个单词:全都大写,单词之间用下划线隔开
java
//final修改基本数据类型,记录的值不能发生改变
//final修饰引用数据类型,记录的地址值不能发生改变,内部的属性值还是可以改变
public class Test {
public static void main(String[] args) {
final Student S = new Student("xzq",40);
S.setName("zrz");
S.setAge(19);
System.out.println(S.getName() + ", " + S.getAge());//zrz, 19
final int[] ARR = {1,2,3,4,5};
ARR[0] = 10;
ARR[1] = 20;
for (int i = 0; i < ARR.length; i++) {
System.out.println(ARR[i]);
}/*10
20
3
4
5*/
}
}
class Student{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
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;
}
}
权限修饰符
用来控制一个成员能被访问的范围的。可以修饰成员变量、方法、构造方法、内部类。
代码块
构造代码块
java
public class Test {
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student("xzq",40);
}
}
/*我要开始构造了
有参构造
我要开始构造了
无参构造*/
class Student{
private String name;
private int age;
//构造代码块
//写在成员位置的代码块
//可以把多个构造方法中重复的代码抽取出来
//每新建一个对象就会执行一次
//先执行构造代码块,再执行构造方法
{
System.out.println("我要开始构造了");
}
public Student() {
System.out.println("有参构造");
}
public Student(String name, int age) {
System.out.println("无参构造");
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;
}
}
java
public class Test {
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student("xzq",40);
}
}
/*
我要开始构造了
有参构造
无参构造
*/
class Student{
private String name;
private int age;
//静态代码块
//随着类的加载而加载,只会被执行一次
//可以用来进行数据初始化
static {
System.out.println("我要开始构造了");
}
public Student() {
System.out.println("有参构造");
}
public Student(String name, int age) {
System.out.println("无参构造");
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;
}
}
抽象类和抽象方法
java
//抽象类不能实例化,不能创建对象
//抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
//可以有构造方法
//抽象类的子类 要么重写抽象类中的所有抽象方法 要么是抽象类
public class Test {
public static void main(String[] args) {
Student s = new Student("xzq",40);
System.out.println(s.getName() + ", " + s.getAge());//xzq, 40
}
}
abstract class Person{
private String name;
private int age;
//抽象类可以有构造方法
//可以在子类创建对象时,给属性进行赋值
public Person() {
}
public Person(String name, int age) {
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;
}
//抽象类的格式,没有大括号
abstract void work();
}
class Student extends Person{
public Student() {}
public Student(String name, int age) {
super(name,age);
}
//抽象类的子类一定要重写抽象方法
@Override
public void work(){
System.out.println("working");
}
}
//抽象类的子类也可以是抽象类,但这样就也不能创建对象,只能再来一个子类创建对象
abstract class student2 extends Person{
}
接口
接口是一种规则,是对行为的抽象。
- 接口用关键字interface来定义:public interface 接口名 {}
- 接口不能实例化
- 接口和类之间是实现关系,通过implements关键字表示:public class 类名 implements 接口名 {}
- 接口的子类(实现类)要么重写接口中的所有抽象方法,要么是抽象类
- 接口和类的实现关系,可以单实现,也可以多实现:public class 类名 implements 接口名1 接口名2 {}
- 实现类还可以在继承一个类的同时实现多个接口。public class 类名 extends 父类 implements 接口名1 接口名2 {}
java
//接口中
//成员变量:只能是常量,默认修饰符:public static final
//没有构造方法
//成员方法:只能是抽象方法,默认修饰符:public abstract
public class Test {
public static void main(String[] args) {
//可以直接调用,说明了默认static
System.out.println(Inter.a);//10
}
}
public interface Inter {
//默认是public static final int a = 10
int a = 10;
//默认是public abstract void method()
void method();
}
java
public interface Inter1 {
public abstract void method1();
public abstract void method2();
}
public interface Inter2 {
public abstract void method1();
public abstract void method2();
}
//可以同时继承多个接口
//当接口中的方法重名时,重写一次即可
public class Inter implements Inter1,Inter2{
@Override
public void method1() {
}
@Override
public void method2() {
}
}
java
public interface Inter1 {
public abstract void method1();
}
public interface Inter2 {
public abstract void method2();
}
//接口之间可以继承,且可以单继承和多继承
public interface Inter3 extends Inter1,Inter2{
public abstract void method3();
}
//实现了子接口,则要重写子接口和所有父接口的抽象方法,如果父接口有父接口,则也要重写抽象方法
public class Inter implements Inter3{
@Override
public void method1() {
}
@Override
public void method2() {
}
@Override
public void method3() {
}
}
java
public interface Inter1 {
public abstract void method1();
//接口中可以有默认方法,default不能省略
//默认方法可以不被重写,如果被重写要去掉default关键字
//如果实现了多个接口,都有同名的默认方法,则子类必须要重写
public default void method2(){
System.out.println("default method");
}
}
public class Inter implements Inter1{
@Override
public void method1() {
System.out.println("method1");
}
//如果要重写是这样的
@Override
public void method2() {
Inter1.super.method2();
}
}
public class Test {
public static void main(String[] args) {
Inter i = new Inter();
i.method1();//method1
i.method2();//default method
}
}
java
public interface Inter1 {
//接口中静态方法,static关键字不能省略
public static void show(){
System.out.println("Inter1 show");
}
}
public class Inter implements Inter1{
//静态方法不能被重写,因为静态方法无法进入虚方法表
//下面这个不是重写,只是又写了个一样名字的方法
public static void show(){
System.out.println("Inter show");
}
}
public class Test {
public static void main(String[] args) {
//静态方法可以直接被类名调用
Inter1.show();
Inter.show();
}
}
java
//私有方法,只能在本类中被调用
public interface Inter1 {
public default void show1(){
System.out.println("Inter1 show1");
show3();
}
public default void show2(){
System.out.println("Inter1 show2");
show3();
}
//普通的私有方法,不用写default,但是是给默认方法服务的
private void show3(){
System.out.println("Inter show3");
}
}
java
public interface Inter1 {
public static void show1(){
System.out.println("Inter1 show1");
show3();
}
public static void show2(){
System.out.println("Inter1 show2");
show3();
}
//静态的私有方法,一定要写static,给静态方法服务
private static void show3(){
System.out.println("Inter show3");
}
}
接口的应用
java
//接口类型 j = new 实现类对象();
//当一个方法的参数是接口时,可以传递该接口所以实现类对象,这叫做接口多态。
java
//适配器设计模式
//如下如果一个接口有五个方法,但是我们只想使用方法3
public interface Inter1 {
public abstract void method1();
public abstract void method2();
public abstract void method3();
public abstract void method4();
public abstract void method5();
}
//可以用一个适配器来实现接口,重写全部接口方法并且全部是空实现
//为防止适配器被创建对象,设为abstract
public abstract class Inter1Adapter implements Inter1{
@Override
public void method1() {
}
@Override
public void method2() {
}
@Override
public void method3() {
}
@Override
public void method4() {
}
@Override
public void method5() {
}
}
//再用我们想要的类去继承适配器,重写想要的方法即可
public class Inter extends Inter1Adapter{
@Override
public void method3() {
System.out.println("new method3");
}
}
内部类
即在一个类的里面,再定义一个类
类的五个成员:属性、方法、构造方法、代码块、内部类
- 内部类表示的事物是外部类的一部分
- 内部类单独出现没有任何意义
java
//内部类可以直接访问外部类的成员,包括私有
//外部类要访问内部类的成员,必须创建对象
public class Car {
String carName;
int carAge;
String carColor;
public void show(){
System.out.println(carName);
//外部类访问内部类成员要先创建对象
Engine e = new Engine();
System.out.println(e.engineName);
}
class Engine{
String engineName;
int engineAge;
public void show(){
//内部类可以直接访问外部类成员,因为默认传this
System.out.println(engineName);
System.out.println(carName);
}
}
}
public class CarTest {
public static void main(String[] args) {
Car c = new Car();
c.carName = "宾利";
c.carAge = 3;
c.carColor = "粉色";
c.show();
}
}
宾利
null
成员内部类
- 写在成员位置的,属于外部类的成员。
- 成员内部类可以被一些修饰符所修饰,比如:private、默认、protected、public、static等
java
//构建内部类对象的方法
public class Outer {
String name;
class Inner{
}
//可以在外部类中写一个方法返回内部类
public Inner getInstance(){
return new Inner();
}
}
public class OuterTest {
public static void main(String[] args) {
//方法一:按以下格式新建对象
//要内部类不是私有的情况下才行
Outer.Inner oi = new Outer().new Inner();
//方法二:创建外部类对象,再调用方法创建内部类对象
//通常内部类是私有时使用该方法,可以用Object接收
Outer ou = new Outer();
Object o = ou.getInstance();
}
}
java
public class Outer {
private int a = 10;
class Inner{
private int a = 20;
public void show(){
int a = 30;
System.out.println(a);//30
System.out.println(this.a);//20
//Outer.this 获取了外部类对象的地址值
System.out.println(Outer.this.a);//10
}
}
public Inner getInstance(){
return new Inner();
}
}
public class OuterTest {
public static void main(String[] args) {
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
内部类的堆内存中有外部类的this,故可以用Outer.this访问外部类的成员变量
静态内部类
- 静态内部类是成员内部类的一种,即有static修饰时
- 静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建外部类对象
java
public class Outer {
int a = 10;
static int b = 20;
static class Inner{
public void show1(){
//静态内部类可以直接访问静态外部类的成员变量
System.out.println(b);
//如果要访问外部类的非静态成员变量,需要创建外部类的对象再访问
Outer o = new Outer();
System.out.println(o.a);
}
}
}
java
public class Outer {
static class Inner{
public void show1(){
System.out.println("no static");
}
public static void show2(){
System.out.println("static");
}
}
}
public class OuterTest {
public static void main(String[] args) {
//创建静态内部类,直接外部类点就行
//只要是静态的东西,都可以用类名直接获取
Outer.Inner oi = new Outer.Inner();
oi.show1();//no static
//调用静态内部类中的静态方法
Outer.Inner.show2();//static
}
}
局部内部类
将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。
java
public class Outer {
int b = 20;
public void show(){
int a = 10;
class Inner{
String name;
int age;
public void method1(){
//该类可以直接访问外部类的成员,也可以访问方法内的局部变量
System.out.println(a);
System.out.println(b);
System.out.println("method1");
}
public static void method2(){
System.out.println("static method2");
}
}
//外界无法直接使用局部内部类
//需要在方法内部创建对象并使用
Inner i = new Inner();
System.out.println(i.name);
System.out.println(i.age);
i.method1();
Inner.method2();
}
}
public class OuterTest {
public static void main(String[] args) {
Outer o = new Outer();
o.show();
}
}
null
0
10
20
method1
static method2
匿名内部类
本质是隐藏了名字的内部类
java
public interface Swim {
public void swim();
}
public abstract class Animal {
public abstract void eat();
}
public class SwimTest {
public static void main(String[] args) {
//实质上大括号及里面的内容才是匿名的内部类
//new Swim()则是创建了一个对象
//这个匿名内部类实现Swim接口,所以要重写swim方法
//所以这个实质上是匿名内部类的对象
new Swim(){
@Override
public void swim() {
System.out.println("override swim");
}
};//后面有个分号
//这个匿名内部类是重写了Animal的抽象方法
//所以这个是继承关系,匿名的内部类继承了Animal类
new Animal(){
@Override
public void eat() {
System.out.println("override eat");
}
};
//当要调用一个方法,但是创建对象只是被传参无意义
//可以用匿名内部类传参
//匿名内部类的使用场景
show(
new Animal() {
@Override
public void eat() {
System.out.println("eating");
}
}
);
//创建一个接口的实现类对象
//接口多态
Swim s = new Swim() {
@Override
public void swim() {
System.out.println("swimming");
}
};
new Swim(){
@Override
public void swim() {
System.out.println("swimming");//swimming
}
}.swim();//用接口的实现类对象调用自己重写的方法
}
public static void show(Animal a){
//编译看左边,运行看右边
a.eat();
}
}
集合
- 集合分为单列集合和双列集合
- 单列集合即一个一个数据添加元素
- 双列集合是一对一对数据添加元素
Collection
- Collection即单列集合,包含List和Set两个
- List序列集合:添加的元素是有序、可重复、有索引
- Set系列集合:添加的元素是无序、不重复、无索引
Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。
java
import java.util.ArrayList;
import java.util.Collection;
public class Test {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<>();
//add()方法
coll.add("aaa");
coll.add("bbb");
//返回布尔值
//如果是往List系列集合添加元素,永远返回true
//如果是往Set系列集合添加元素,集合中元素不存在返回true,集合中元素存在返回false
//因为Set系列集合不允许重复
System.out.println(coll.add("ccc"));//true
System.out.println(coll);//[aaa, bbb, ccc]
//clear()方法
coll.clear();
System.out.println(coll);//[]
coll.add("aaa");
coll.add("bbb");
//集合元素存在返回true,不存在则返回false
System.out.println(coll.remove("aaa"));//true
System.out.println(coll);//[bbb]
//集合中有这个元素则为true,否则为false
//contains()方法底层用equals()方法判断,故若集合中存储的是自定义对象,需要重写equals()方法
System.out.println(coll.contains("bbb"));//true
System.out.println(coll.isEmpty());//false
System.out.println(coll.size());//1
}
}
java
//Collection的contains()方法底层用equals()方法判断,故若集合中存储的是自定义对象,需要重写equals()方法
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
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;
}
//可以用alt加insert插入重写的equals()方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
}
public class StudentTest {
public static void main(String[] args) {
Student s1 = new Student("xzq",40);
Collection<Student> coll = new ArrayList<>();
coll.add(s1);
Student s2 = new Student("xzq",40);
//如果没有重写equals()方法则为false,因为Object类中的equals方法判断的是地址值
System.out.println(coll.contains(s2));//true
}
}
迭代器遍历
迭代器在Java中的类是Iterator,迭代器是集合专用的遍历方式。
迭代器在遍历集合的时候是不依赖索引的,是用指针。
java
//通常需要删除元素时才使用迭代器遍历
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Test {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<>();
coll.add("aaa");
coll.add("bbb");
//创建迭代器对象,默认指向0索引处
Iterator<String> it = coll.iterator();
//hasNext()方法判断当前位置是否有元素
while(it.hasNext()){
//next()方法有两个作用:获取当前位置的元素,指针移到下一个位置
String str = it.next();
//这个if语句会报错
//迭代器遍历的时候,不能用集合的方法进行增加或者删除
if("bbb".equals(str)){
coll.remove("bbb");
}
//可以用迭代器带的remove方法
if("bbb".equals(str)){
it.remove();
}
System.out.println(str);//aaa
}
//注意点:迭代器遍历完毕,指针不会复位
}
}
增强for遍历
- 增强for遍历的底层就是迭代器,为了简化迭代器的代码书写的
- 所有单列集合和数组才能用增强for进行遍历
- 在用增强for遍历时,也不能用集合的方法进行添加和删除
java
public class ForTest {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<>();
coll.add("xzq");
coll.add("love");
//快捷键:coll.for
//s只是一个第三方变量,改变其值不会改变集合中原本的数据
for(String s : coll){
System.out.println(s);
}
}
}
Lambda表达式遍历
java
public class ForTest {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<>();
coll.add("xzq");
coll.add("love");
//利用匿名内部类
//底层也是遍历集合,依次得到每一个元素
//把得到的每一个元素传递给下面的accept方法
//s表示集合中的每一个数据
coll.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
//xzq
//love
});
//Lambda表达式
coll.forEach((String s) -> {
System.out.println(s);
});
//简化写法,可以不写数据类型,且方法体只有一行
coll.forEach(s -> System.out.println(s));
}
}
List集合
- 有序、有索引、可重复
- Collection的方法List都继承了
- List集合因为有索引,所以多了很多索引操作的方法
java
public class ListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("xzq");
list.add("love");
list.add("you");
System.out.println(list);//[xzq, love, you]
//List集合有的在指定索引处添加元素
list.add(1,"i");
System.out.println(list);//[xzq, i, love, you]
//返回被删除的元素
System.out.println(list.remove(3));//you
list.remove("xzq");
System.out.println(list);//[i, love]
//返回被替代的元素
System.out.println(list.set(0,"qqq"));//i
System.out.println(list);//[qqq, love]
//获得指定索引处的元素
System.out.println(list.get(1));//love
}
}
java
public class IntRemoveTest {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
//remove方法有两种参数传递类型,见上面的图
//默认是删除1索引的
//本质:在调用方法的时候,如果方法出现了重载,会优先调用实参和形参一致的方法
list.remove(1);
//如果要删除Object的,需要先封装再传递
//即可以删除值1
Integer i = Integer.valueOf(1);
list.remove(i);
System.out.println(list);
}
}
List系列集合的五种遍历方式:
- 迭代器(在遍历过程中需要删除元素)
- 列表迭代器(在遍历过程中需要添加元素)
- 增强for循环(只是想遍历)
- Lambda表达式遍历(只是想遍历)
- 普通for循环(遍历的时候想操作索引)
java
//列表迭代器
public class ListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("xzq");
list.add("love");
list.add("you");
//创建列表迭代器对象
ListIterator<String> it = list.listIterator();
//和Collection不同的是多了个add()方法
while(it.hasNext()){
String str = it.next();
//remove()方法直接删去str元素
if("love".equals(str)) {
it.remove();
}
//add()方法在str元素后面一个位置加上新元素
if("you".equals(str)){
it.add("tt");
}
}
System.out.println(list);//[xzq, you, tt]
}
}
ArrayList集合
集合和数组对比:
- 数组长度固定,集合长度可变
- 数组可以存基本数据类型和引用数据类型,集合可以存引用数据类型,如果要存基本数据类型,要变成包装类后才可以存
java
public class Demo1 {
public static void main(String[] args) {
//集合是Java已经写好的一个类,底层做了处理,打印出来是里面的数据值,且会有[]将数据值包裹起来
ArrayList<String> list = new ArrayList<>();
System.out.println(list);
}
}
[]
基本方法
java
public class Demo1 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
//增加操作
//返回值是boolean,永远是true
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
System.out.println(list);//[aaa, bbb, ccc, ddd]
//删除操作
//下面这个返回被删除的数据
String s1 = list.remove(0);
//下面这个若数据存在返回true,否则返回false
boolean b1 = list.remove("ccc");
System.out.println(s1);//aaa
System.out.println(list);//[bbb, ddd]
//修改操作,将对应索引的值更改
//返回被修改的数据
String s2 = list.set(0,"eee");
System.out.println(s2);//bbb
System.out.println(list);//[eee, ddd]
//查询操作
//返回被查询的数据
String s3 = list.get(0);
System.out.println(s3);//eee
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//eee
//ddd
}
}
java
public class Demo3 {
public static void main(String[] args) {
//使用包装类
ArrayList<Integer> list = new ArrayList<>();
//int和Integer类型可以相互转化
list.add(1);
list.add(7);
list.add(1);
list.add(7);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
1
7
1
7
java
//调用Student类创建集合
public class StudentTest {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<>();
Scanner sc = new Scanner(System.in);
for(int i = 0;i<3 ;i++){
String name = sc.next();
int age = sc.nextInt();
Student s = new Student(name,age);
list.add(s);
}
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i).getName() + ", " + list.get(i).getAge());
System.out.println();
}
}
}
xzq 40
zrz 19
xxg 5
xzq, 40
zrz, 19
xxg, 5
//创建Student类
public class Student {
private String name;
private int age;
public Student(){}
public Student(String name,int age){
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;
}
}
java
public class AddAllTest {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
list1.add("xzq");
list1.add("love");
ArrayList<String> list2 = new ArrayList<>();
list2.add("i");
//addAll()方法,将一个集合中全部元素加入另一个集合
list2.addAll(list1);
System.out.println(list2);
}
}
ArrayList集合底层原理
- 利用空参创建的集合,在底层创建一个默认长度为0的数组
- 添加第一个元素时,底层会创建一个新的长度为10的数组
- 存满时,会扩容1.5倍
- 如果一次性添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准
泛型
- 格式:<数据类型>
- 泛型只能支持引用数据类型,如果要加int要写Integer
- 泛型在编译时可以指定数据类型,但是在字节码文件中仍然是Object类型
java
//没泛型时,可以往集合加任何数据
public class ArrayListTest {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(123);
list.add("aaa");
list.add(new Student("xzq",40));
Iterator it = list.iterator();
while(it.hasNext()){
//多态的弊端是不能访问子类的特有功能
//所以我们无法调用特有行为,强转也又得无法转
//所以泛型很重要
Object o = it.next();
System.out.println(o);
}
}
}
123
aaa
collection.Student@4554617c
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
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 boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
}
泛型类
当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类。
java
//自己写一个泛型类,不确定数据类型,可以用任意大写字母表示,通常为E
public class MyArrayList<E> {
Object[] obj = new Object[10];
int size;
public boolean add(E e){
obj[size] = e;
size++;
return true;
}
public E get(int index){
//要将Object类型强转为E类型
return (E)obj[index];
}
@Override
public String toString() {
return Arrays.toString(obj);
}
}
public class MyArrayListTest {
public static void main(String[] args) {
//此时E为String
MyArrayList<String> list1 = new MyArrayList<>();
list1.add("xzq");
list1.add("love");
System.out.println(list1);//[xzq, love, null, null, null, null, null, null, null, null]
//此时E为Integer
MyArrayList<Integer> list2 = new MyArrayList<>();
list2.add(717);
list2.add(1717);
System.out.println(list2.get(1));//1717
System.out.println(list2);//[717, 1717, null, null, null, null, null, null, null, null]
}
}
泛型方法
方法中形参类型不确定时,可以使用类名后面定义的泛型(所有方法都能用),或者在方法上申明定义自己的泛型(只有本方法能用)。
java
//定义一个工具类
public class ListUtil {
private ListUtil(){}
//泛型方法
//泛型放在修饰符后面
//因为无法确定集合泛型故也要在方法前加泛型才能传入参数
//E...e其实是传入一个数组,可以是任意长度
public static<E> void addAll(ArrayList<E> list,E...e){
for (int i = 0; i < e.length; i++) {
list.add(e[i]);
}
}
}
public class ListUtilTest {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
ListUtil.addAll(list1,"xzq","i","love");
System.out.println(list1);//[xzq, i, love]
ArrayList<Integer> list2 = new ArrayList<>();
ListUtil.addAll(list2,1017,1317,1717,17171);
System.out.println(list2);//[1017, 1317, 1717, 171717]
}
}
泛型接口
当一个接口中,某个变量的数据类型不确定时,就可以定义带有泛型的接口。
如何使用一个带泛型的接口:
- 方法一:实现类给出具体类型
- 方法二:实现类延续泛型,创建对象时再确定类型
java
//方法一:实现类给出具体类型
public class MyArrayList implements List<String> {
//重写方法
}
public class MyArrayListTest {
public static void main(String[] args) {
//此时创建对象不用写泛型,因为已经确定了
MyArrayList list = new MyArrayList();
}
}
//方法二:实现类延续泛型,创建对象时再确定类型
public class MyArrayList<E> implements List<E> {
//重写方法
}
public class MyArrayListTest {
public static void main(String[] args) {
//此时创建对象时需要确定类型
MyArrayList<String> list = new MyArrayList();
}
}
java
//泛型不具备继承性,但是数据具备继承性
public class Generics {
public static void main(String[] args) {
ArrayList<Ye> list1 = new ArrayList<>();
ArrayList<Fu> list2 = new ArrayList<>();
ArrayList<Zi> list3 = new ArrayList<>();
//只能传list1,传list2和list3会报错
method(list1);
//数据有继承性,多态
list1.add(new Ye());
list1.add(new Fu());
list1.add(new Zi());
}
public static void method(ArrayList<Ye> list){
}
}
class Ye{}
class Fu extends Ye{}
class Zi extends Fu{}
java
//如果我们想传集合且集合里面是Ye或者Fu或者Zi
//可以使用泛型的通配符
// ?也表示不确定的类型
// 他可以进行类型额限定
// ? extends E:表示可以传递E或者E所有的子类类型
// ? super E:表示可以传递E或者E所有的父类类型
public class Generics {
public static void main(String[] args) {
ArrayList<Ye> list1 = new ArrayList<>();
ArrayList<Fu> list2 = new ArrayList<>();
ArrayList<Zi> list3 = new ArrayList<>();
method(list1);
method(list2);
method(list3);
}
//利用泛型的通配符
public static void method(ArrayList<? extends Ye> list){
}
}
class Ye{}
class Fu extends Ye{}
class Zi extends Fu{}
//应用场景
//1.如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型、泛型方法、泛型接口。
//2.如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以使用泛型的通配符
//泛型的通配符关键点:可以限定泛型的范围
Set
Set接口中的方法基本上与Collection的API一致
- HashSet:无序、不重复、无索引
- LinkedHashSet:有序、不重复、无索引
- TreeSet:可排序、不重复、无索引
java
public class SetTest {
public static void main(String[] args) {
Set<String> s = new HashSet<>();
//Set系列集合不重复,若集合中元素不存在为true,反之为false
s.add("张三");
System.out.println(s.add("张三"));//false
s.add("李四");
s.add("王五");
//Set系列集合是无序的
System.out.println(s);//[李四, 张三, 王五]
//迭代器遍历
Iterator<String> it = s.iterator();
while(it.hasNext()){
String str = it.next();
System.out.println(str);
}
//增强for遍历
for (String str : s) {
System.out.println(str);
}
//Lambda表达式遍历,匿名内部类
s.forEach(new Consumer<String>() {
@Override
public void accept(String str) {
System.out.println(str);
}
});
//Lambda表达式遍历
s.forEach(str -> System.out.println(str));
}
}
李四
张三
王五
哈希值
- 根据hashCode方法算出来的int类型的整数
- 该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
- 一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值
对象的哈希值特点
- 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
- 如果已经重写HashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
- 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)
java
public class HashTest {
public static void main(String[] args) {
Student s1 = new Student("xzq",40);
Student s2 = new Student("xzq",40);
//如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
//是根据地址值计算的
System.out.println(s1.hashCode());//1163157884
System.out.println(s2.hashCode());//1956725890
}
}
java
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
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;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
//重写hashCode方法
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class HashTest {
public static void main(String[] args) {
Student s1 = new Student("xzq",40);
Student s2 = new Student("xzq",40);
//如果已经重写HashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
System.out.println(s1.hashCode());//3696666
System.out.println(s2.hashCode());//3696666
//哈希碰撞
System.out.println("abc".hashCode());//96354
System.out.println("acD".hashCode());//96354
}
}
HashSet底层原理
- HashSet是集合底层采取哈希表存储数据
- 哈希表是一种对于增删改查数据性能都较好的结构
哈希表组成
- JDK8之前:数组+链表
- JDK8开始:数组+链表+红黑树
-
创建一个默认长度16,默认加载因子为0.75(用于扩容)的数组,数组名为table
-
根据元素的哈希值跟数组的长度计算出应存入的位置
int index = (数组长度 - 1) & 哈希值;
-
判断当前位置是否为null,如果是null直接存入
-
如果位置不为null,表示有元素,则调用equals方法比较属性值
-
一样:不存 不一样:存入数组,形成链表
JDK8以前:新元素存入数组,老元素挂在新元素下面
JDK8以后:新元素直接挂在老元素下面
- JDK8以后,当链表长度超过8 ,而且数组长度大于等于64时,自动转换为红黑树
- 如果集合中存储的是自定义对象,必须要重写hashCode和equals方法