问题:
修饰符分为权限修饰符和非权限修饰符
public protected ,缺省 , private
static ,final ,abstract ,
基本数据类型只是数字,什么也干不了,需要使用包装类型,可以查看长度,
public class Main{
public static void main(String [] args){
System.out.println("The size of short is "+Short.BYTES+" bytes.");
System.out.println("The size of short is "+Short.SIZE/8+" bytes.");
System.out.println("The size of int is "+Integer.BYTES+" bytes.");
System.out.println("The size of int is "+Integer.SIZE/8+" bytes.");
System.out.println("The size of long is "+Long.BYTES+" bytes.");
System.out.println("The size of long long is "+Long.BYTES+" bytes.");
}
}
十进制转化为八进制
public class Main{
public static void main(String [] args){
Integer a = 1234;
System.out.printf("0"+"%o",a); //八进制
System.out.printf(Integer.toOctalString(a));
System.out.printf(" 0X"+"%X",1234); //十六进制
System.out.printf(Integer.toHexString(a));
}
}
next(),不能获得带空格的字符串
nextLine(),方法可以获得带空格的字符串
最大公约数,最小公倍数
- 最大公约数 不能大于 最小值(取模等于零),使用break否则就输出所有的
- 最小公倍数 不能小于最大值,不能大于 两数之和,使用break否则就输出所有的值了
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("输入第一个正整数:");
int m = sc.nextInt();
System.out.println("输入第二个正整数:");
int n = sc.nextInt();
if (m <n){
int temp = m;
m = n;
n = temp;
}
//最大公约数
for (int i = n;i>=1;i--){
if(m % i ==0 && n % i ==0){
System.out.println("最大公约数:" + i);
break; //一旦在循环中使用break,退出循环
}
}
//最小公倍数
for (int i = m;i<=m*n;i++ ){
if (i % m == 0 && i % n == 0){
System.out.println("最大公倍数:"+i);
break;
}
}
}
}
水仙花数
-
三位数的百位数的三次方+十位数的三次方+个位数的三次方 == 这个三位数
-
表示平方的为pow(2,3) ----->2^3
public class Main {
public static void main(String[] args) {
for (int i = 100 ;i <= 999;i++ ){
int ge = i % 10;
int shi = (i / 10) % 10;
int bai = (i / 100);
//System.out.println(bai+shi+ge);
int shui =(int) ((Math.pow(ge,3))+(Math.pow(shi,3)+Math.pow(bai,3)));
if (i == shui){
System.out.println(shui);
}
}
}
}
输入一堆数,判断正数个数和负数个数,输入0时结束
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int positiveNumber = 0;
int negativeNumber = 0;
while (true) {
int a = sc.nextInt();
if (a > 0) {
positiveNumber += 1;
} else if (a < 0) {
negativeNumber += 1;
} else {
break;
}
}
System.out.println(positiveNumber);
System.out.println(negativeNumber);
}
九九乘法表
public static void main(String[] args) {
for (int i =1;i<=9;i++){
for (int j = 1;j<=i;j++){
System.out.print(j +"*"+ i + "=" +i*j+" ");
}
System.out.println();
}
}
遍历一百以内的质数()
- 质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
public static void main(String[] args) {
boolean isFlag = true; //判断是否除尽的标志
long start = System.currentTimeMillis();
for (int i =2;i<=100;i++){ //遍历2--100
//for (int j = 2; j < i;j++){ //内层范围(2--i-1)
for (int j = 2; j <= Math.sqrt(i);j++){ //优化2
if (i%j==0){ //如果能够除尽将isFlag状态改为false
isFlag = false;
//break; //优化1
}
}
if (isFlag == true){ //输出没有改变的
System.out.println(i);
}
isFlag = true; //将状态改为true
}
long end = System.currentTimeMillis();
System.out.println("花费时间为:" + (end-start)); //14290 //1601 //258
}
完数
- 一个数恰好等于因子之和,这个数就为“完数”,6 = 1 + 2 + 3
public static void main(String[] args) {
int sum = 0;
for (int i = 1;i<=1000;i++){
for (int j = 1;j <= i/2;j++){ j<i/2
if (i%j==0){
sum +=j;
}
}
if (sum == i){
System.out.println(i);
}
sum = 0;
}
}
杨辉三角
-
使用二维数组的内层循环时,应该先赋值,
-
在赋值循环里将各个值填入
-
重点在于最后只是单纯的循环二维数组里的值,
-
int [][] arr= new int[10][]; for (int i = 0;i<arr.length;i++) { arr[i] = new int[i + 1];//使用二维数组先把空间开辟 arr[i][0] = 1; arr[i][i] = 1; if (i >= 2) { //赋值 for (int j = 1; j < arr[i].length - 1; j++) { // arr[i][j] = arr[i-1][j] + arr[i-1][j-1]; arr[i][j] = arr[i-1][j] + arr[i-1][j-1]; } } } //遍历二维数组 for (int i = 0;i<arr.length;i++){ for (int j = 0;j<arr[i].length;j++){ System.out.print(arr[i][j]+" "); } System.out.println(); }
一维数组赋值操作
-
创建一个长度为6的int型数组,要求数组元素的值在1~30之间,且是随机赋值。同时要求各元素的值不同.
-
赋值操作时最后遍历数组
int arr[] = new int[6]; for (int i = 0;i<arr.length;i++){ arr[i] = (int) (Math.random() * 30) +1; for (int j = 0;j<i;j++){ if (arr[i] == arr[j]){ i--; //如果第二个值和第一个值相同,直接跳出然后重新赋值 //可能需要和前边多次比较 break; } } } for (int i = 0;i<arr.length;i++){ System.out.println(arr[i]); }
array1赋值array2
- 堆中只存放这一组数据,
- new一次只有一个数组
int[] array1,array2;
array1 = new int[]{2,3,5,7,11,13,17,19};
for (int i = 0; i < array1.length; i++) {
System.out.print(array1[i]+" ");
}
System.out.println();
array2 = array1; //这个相当于把地址给了array2,
for (int i = 0; i < array2.length; i++) {
if (i%2==0){
array2[i] = i;
}
System.out.print(array2[i]+" ");
}
System.out.println();
for (int i = 0; i < array1.length; i++) {
System.out.print(array1[i]+" ");
}
// 2 3 5 7 11 13 17 19
// 0 3 2 7 4 13 6 19
// 0 3 2 7 4 13 6 19
数组的反转
- 一定要记得除以2,否则就会全部遍历一遍,数组又换回来了
int[] array1;
array1 = new int[]{2,3,5,7,11,13,17,19};
for (int i = 0; i < array1.length/2; i++) {
int temp = array1[i];
array1[i] = array1[array1.length-1-i];
array1[array1.length-1-i] = temp;
}
for (int i = 0; i < array1.length; i++) {
System.out.print(array1[i]+" ");
}
二分法
- 要求必须有顺序
int[] array1;
array1 = new int[]{2,3,5,7,11,13,17,19};
int description = 4;
int head = 0;
int end = array1.length-1;
boolean flag = true;
while (head <= end){
int middle = (head + end)/2;
if (array1[middle] == description){
flag = false;
System.out.println("找到了:"+middle);
break;
}else if (description < array1[middle]){ //
end = middle-1;
}else{
head = middle + 1; //
}
}
if (flag){
System.out.println("没有这个数据!");
}
冒泡排序
-
相邻两个数之间进行比较,然后交换数据
-
int[] array1; array1 = new int[]{2,3,18,7,34,13,1,17,19}; System.out.print("遍历前:"); for (int i : array1){ System.out.print( i +" "); } System.out.println(); for(int i = 0;i < array1.length-1;i++){ //外层控制次数 for (int j = 0;j <array1.length-i-1;j++){ //内层用来交换数据 if (array1[j]>array1[j+1]){ //如果前边这个比后边这个大就交换 int temp = array1[j]; array1[j] = array1[j+1]; array1[j+1] = temp; } } System.out.print("第"+(i+1)+"次排序:"); for (int j = 0; j < array1.length; j++) { System.out.print(array1[j] + " "); } System.out.println(); } System.out.print("冒泡排序后:"); for (int j = 0; j < array1.length; j++) { System.out.print(array1[j]+" "); }
对象数组
- new 20个Student对象
- 随机对年级,分数赋值
- 将三年级的输出
- 按成绩进行冒泡排序
- 引用数据类型的值,要么是null,要么是地址值;数组的情况,意思就是你要是想用它就必须自己实例化
public class Main {
public static void main(String[] args) {
Student [] students = new Student[20];//仅仅是分配了一个null值的空间
for (int i =0;i<students.length;i++){
students[i] = new Student(); //new了20个对象
students[i].number = i + 1; //[1,20]
students[i].state = (int) (Math.random() * (6 - 1 + 1) +1); //[0,6]
students[i].score = (int) (Math.random() * (100 - 0 +1) + 0); //[0,100]
students[i].info();
}
System.out.println("*****************************");
for (int i =0;i<students.length;i++){
if (students[i].state == 3 ){
students[i].info();
}
}
//按成绩冒泡排序
for (int i =0;i<students.length;i++){
for (int j = 0;j < students.length-i-1;j++){
if (students[j].score>students[j+1].score){
Student temp = students[j]; //temp的类型应该为Student对象
students[j] = students[j+1];
students[j+1] = temp;
}
}
}
System.out.println("遍历后:");
for (int i =0;i<students.length;i++){
students[i].info();
}
}
}
class Student{
int number;
int state;
int score;
public void info(){
System.out.println("学号:" + number + "年级:" + state + "分数:" + score);
}
}
改造后:
public class Main {
public static void main(String[] args) {
Student [] students = new Student[20];
for (int i =0;i<students.length;i++){
students[i] = new Student(); //new了20个对象
students[i].number = i + 1; //[1,20]
students[i].state = (int) (Math.random() * (6 - 1 + 1) +1); //[0,6]
students[i].score = (int) (Math.random() * (100 - 0 +1) + 0); //[0,100]
students[i].info();
}
System.out.println("*****************************");
Main main = new Main(); //方法定义在了Main中,所以想要使用就必须实例Main
//遍历
main.print(students);
//年级为3年级的
System.out.println("******************");
main.san(students,3);
//按成绩冒泡排序
main.sort(students);
System.out.println("遍历后:");
main.print(students);
}
public void print(Student[] students){
for (int i =0;i<students.length;i++){
students[i].info();
}
}
public void san(Student[] students,int state){
for (int i =0;i<students.length;i++){
if (students[i].state == state){
students[i].info();
}
}
}
public void sort(Student[] students){
for (int i =0;i<students.length;i++){
for (int j = 0;j < students.length-i-1;j++){
if (students[j].score>students[j+1].score){
Student temp = students[j]; //temp的类型应该为Student对象
students[j] = students[j+1];
students[j+1] = temp;
}
}
}
}
}
class Student{
int number;
int state;
int score;
public void info(){
System.out.println("学号:" + number + "年级:" + state + "分数:" + score);
}
}
对象数组的内存解析:
实现trim()方法
String s = " a bc de ";
System.out.println(s.length());
System.out.println(return1(s));
System.out.println(return1(s).length());
}
public static String return1(String string){
int start = 0;
int end = string.length()-1;
for (start = 0; start < string.length() && start <= end; start++){
char s = string.charAt(start);
if (s == ' '){
start++;
}else {
break;
}
}
for (end = string.length() - 1; end>=0 && end >= start; end--){
char s = string.charAt(end);
if (s == ' '){
end--;
}else {
break;
}
}
String s2 = string.substring(start,end+1);
return s2;
}
实现指定字符串的反转
public static void main(String[] args) {
String s = new String("abcdefg");
System.out.println(s);
reserve(s,2,5);
System.out.println(reserve(s,2,6));
}
public static String reserve(String str,int index,int end){
String s = str.substring(index,end);
StringBuffer stringBuffer = new StringBuffer(s);
StringBuffer stringBuffer1 = new StringBuffer();
stringBuffer1.append(str.substring(0,index));
stringBuffer1.append(stringBuffer.reverse());
stringBuffer1.append(str.substring(end,str.length()));
String s1 = new String(stringBuffer1);
return s1;
}
返回指定字符串在指定字符中出现的次数
public static void main(String[] args) {
String s1 ="ab";
String s2 = "abaablksjdgkjasdabdkflsabjf";
System.out.println(getCount(s2, s1));
}
public static int getCount(String s1,String s2){
int index = 0;
int count = 0;
if (s1.length() >= s2.length()){
while (s1.indexOf(s2)!=-1){
index = s1.indexOf(s2);
s1 = s1.substring(index + s2.length());
count++;
}
return count;
}else {
return 0;
}
}
变量:
A = 65,a = 97
包含变量类型,变量名和存储的值(int a = 0)
java中每个变量必须先声明,后使用
八种基本数据类型:byte,short,int,long,float,double,boolean,char
引用数据类型:类(class),接口(interface),数组(【】)
按声明位置不同分为成员变量和局部变量
- 在方法体外,类体内声明的变量称为成员变量。
- 在方法体内部声明的变量称为局部变量
成员变量:
- 实例变量(不以static修饰)
- 静态变量(以static修饰)
整数类型:(定义long时,必须以“l”或“L”结尾,且结果不显示后缀l)
- byte 1字节=8bit位 -128——127 256个
- short 2字节 -215——(215)-1 2^16个
- int 4字节 -231——(231)-1 2^32个
- long 8字节 -263——(263)-1 2^64个
浮点型:(定义float时,必须以”f“或”F“结尾,且后缀不带f)
- 单精度float 4字节
- 双精度double 8字节
字符型:
- char 1字符==2字节 char进行运算的时候是数字,否则就是字符本身
- 定义char型变量,通常使用一对 ’ ‘,且只能存放一个字符,不能不放
- 转义字符 char c1 = ‘\n’;
- \ 表示转义字符, / 表示根目录
布尔类型:
- 只有true和false
System.out.println("你参加不了单身party了!") \\你参加不了单身party了!
System.out.println("你参加不了\n单身party了!") \\你参加不了
\\单身party
System.out.println("你参加不了\\n单身party了!"); \\你参加不了\n单身party了!
System.out.println("你参加不了\"单身\"party了!"); \\你参加不了"单身"party了!
基本数据类型之间的运算规则:(整型常量默认int,浮点型默认double)
1.自动类型提升:
- 当容量小的数据类型的变量与容量大的数据类型进行运算时,结果自动提升为容量大的数据类型。
- byte、char、short–>int–>long–>float–>double
- 特别的:当byte、char、short三种类型的变量做运算时,结果为int型
2.强制类型转化
-
byte b = 127 ; b++; System.out.println(b); //-128 看二进制
String类型:
-
String属于引用数据类型,字符串
-
声明String类型变量时,使用一对“”
-
String可以和八种基本数据类型做运算,且运算只能是连接运算
-
运算的结果仍是String
-
String s = 123; //no String s2 = 123 +""; //"123" String s1 = "Hello"; int num = 10; char c = 'a'; System.out.println( s1 + num + c);//Hello10a System.out.println(num + c +s1);//107Hello System.out.println(c + s1 + num);//aHello10
-
//因为char类型默认要转化为int char c1 = ' '; System.out.println("*" + c1 + "*"); //* * System.out.println("* *"); //* * System.out.println('*' + '\t' + '*'); //93 System.out.println('*' + "\t" + '*'); //* * System.out.println("*" + '\t' + '*'); //* * System.out.println('*' + '\t' + "*"); //51*
二进制: 以0b或0B开头
-
最前边的一位是符号位(0表示正数,1表示负数)
-
正数:原码,补码,反码都是一样的
-
负数:
-
1 0001110 -14的原码 原码到反码为按位取反 1 1110001 -14的反码 反码到补码为加 1 1 1110010 -14的补码
-
计算机都是以补码方式存储数据
八进制: 以0开头
十六进制:以0X或0x开头
运算:
取模时,结果符号与分子相同
int a = -23;
int b = 13;
int c = -12;
System.out.println(a%b); //-10
System.out.println(b%c); //1
int a = 10;
int b = 3;
double sum = (double)a/b //3.4
++a 先++,后运算
short s1 = 10;
s1++; //自增一不会改变本身变量的数据类型
byte b1 = 127;
b1++; //-128
&逻辑与 ,&&短路与
- &左边不正确后边还要走,&&左边不正确右边不运算
<<
int i = 21;
System.out.println(i<<3) 21*2^3
System.out.println(i>>3) 21/2^3
三元运算符:
(条件表达式)?表达式1:表达式2;
表达式1和表达式2为同种类型,如果不是同种类型但存在自动提升也可以
Object object = true?new Integer(1):new Double(2.0); //自动类型提升
System.out.println(object); //1.0
Scanner:
-
输入多组数据时
-
System.out.println("请输入第一个数:"); int a = sc.nextInt(); System.out.println("请输入第二个数:"); int b = sc.nextInt(); System.out.println("请输入第三个数:"); int c = sc.nextInt();
Math:
-
0.0<=Math.random()<1.0 返回的类型为double类型 ,[0.0,1.0)
-
取值范围10~99
-
Math.random()*100 范围为[0.0,100.0)
-
Math.random()*90 [0.0,90.0)
-
Math.random()*90 + 10 [10.0,100.0)
-
[a,b] (int)(Math.random()*(b-a+1)+a)
Switch:
- 根据switch表达式的值,依次匹配各个case中的常量,一旦匹配成功,则进入相应的case中,调用执行语句。
- 当执行完语句后,则仍然依次执行其他的case语句,直到遇到break或程序执行完毕
- switch结构中的表达式,只能是6种数据类型(byte,short,char,int,枚举,String),不能是浮点型,布尔类型
循环结构:
-
初始化条件,循环条件,循环体,迭代条件
-
for(①;②;④) { ③; }
-
public static void main(String[] args) { int num = 1; for(System.out.print('a');num<=3;System.out.print('c'),num++) System.out.print('b');//abcbcbc }
-
① while(②){ ③ ④ }
-
① do{ ③ ④ }while(②)
-
do-while至少执行一次循环体
-
执行多次的时候while和do-while没什么区别
break和continue和return
- break 在 循环结构和switch-case中 使用 结束当前循环
- continue 在 循环结构中 使用 结束本次循环
- return在 方法体内 使用 结束本次方法,如果有返回值,返回数值
- break和continue,return之后不能声明执行语句
public static void main(String[] args) {
for (int i =1;i<= 10;i++){
if (i%4==0){
//break; //123
continue; //123567910
}
System.out.print(i);
}
}
数组:
一维数组
-
是多个相同类型数据按照一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对数据进行统一管理。
-
数组是引用数据类型,创建数组可以是基本数据类型也可以是引用数据类型
-
创建数组对象会在内存中开辟出一整块连续的空间,而数组名引用的是这块连续空间的首地址
-
数组长度一旦确定就不会被修改
-
静态初始化和动态初始化(赋值的叫静态没赋值叫动态)
int []ids = new int[]{1001,1002,1003,1004};//静态初始化 int []ids = new int[5];//动态初始化
-
char[] arr 默认是0不是‘0’;
-
int [] arr= new int[4]; System.out.println(arr); //[I@7ea987ac System.out.println(arr[0]); //0
-
boolean类型默认值为false
二维数组
- 当没有赋值时
int [][] arr= new int[4][3];
1.外层默认值为地址 内层默认值为该数据类型的默认值
int [][] arr1 = new int[4][];
内层默认值为null,因为是个数组
外层空指针异常
int [][] arr= new int[4][3];
System.out.println(arr); //[I@7ea987ac
System.out.println(arr[0]); //[I@12a3a380
System.out.println(arr[0][0]); //0
//没指名行值时
int [][] arr1 = new int[4][];
System.out.println(arr1); //[[I@29453f44
System.out.println(arr1[0]); //null 因为数组是一个引用数据类型,所以内部时null,二维数组里存放的时数组,
System.out.println(arr1[0][1]); //NullPointerException
arrays工具类
面向对象:
堆:存放的时实例对象
栈:就是虚拟机栈,用于存储局部变量
方法区:常量池,静态方法区,类信息。
每个线程用于自己独立的:栈,程序计数器
多个线程,共享同一个进程的:方法区,堆
Java类及类成员:
基础:
- (属性,方法,构造器,代码块,内部类)
- 类:对一类事物的描述,是抽象的,概念上的定义;
- 对象:是实际存在的该类事物的每个个体,因为也被称为实例(instance)
- 面向对象程序设计的重点是类的设计,设计类就是设计类成员
操作:
局部变量和成员变量的区别:
-
相同点:
- 定义变量的格式 数据类型 变量名 = 变量值
- 先声明,后使用
- 变量都有其自己的作用域
-
不同点:
- 在类中声明的位置不同
- 局部变量:方法内,方法形参,代码块,构造器,构造器形参
- 成员变量:定义在一组类{}中
- 关于权限修饰符的不同:
- 成员变量:可以在声明成员变量时,指明权限,常用的权限修饰符:private,缺省,protected,public
- 局部变量不能使用权限修饰符
- 默认初始化值:
- 成员变量:byte,char,short,int,long = 0,float,double = 0.0,boolean = false
- 局部变量:没有默认的初始化值,使用前,必须先赋值。调用形参时,在方法赋值就可以
- 在内存中的位置:
- 成员变量:加载在堆内存中(非static)
- 局部变量:加载在栈空间中
- 在类中声明的位置不同
-
引用数据类型的值,要么是null,要么是地址值;数组的情况,意思就是你要是想用它就必须自己实例化
-
匿名对象的使用
- new 对象();创建一个对象的时候没有显式的给他一个变量名。即匿名对象
- 匿名对象只能调用一次
-
方法的重载:
- 在同一个类中,允许出现一个以上同名的方法,只要他们的参数类型,参数个数不同即可。
- 判断是否重载:跟方法的权限修饰符,返回值类型,形参,方法体都没有关系
-
重写:子类可以根据需要对从父类中继承的方法进行改造,在程序执行时,子类方法将覆盖父类的方法
- 形参列表相同
- 子类方法的权限修饰符不小于父类被重写的方法的权限修饰符,子类不能重写父类中声明为private权限的方法
- 父类返回值类型为。。。,子类重写时,可以声明为Object
- 子类和父类的同名同参数的方法要么都声明为非static(考虑重写),static(不是重写)
-
值传递:
- 如果参数类型时基本数据类型,此时实参赋给形参的是实参真是存储的数据值。
- 如果参数类型时引用数据类型,此时实参赋给形参的是实参真是存储的地址值。
- 基本数据类型的数据值出了规定范围就没有了,引用的是对地址里的值进行改变
public static void main(String[] args) {
int[] arr = new int[20];
System.out.println(arr);
char[] arr1 = new char[]{'a','b'};
System.out.println(arr1); //ab而不是0,因为定义了一个char【】的方法
}
面向对象的特征:
- (封装,继承,多态)(抽象)
封装性的体现
-
我们将类的属性私有化(private),同时,提供公共的(public)的接口去获取getXxx()去获取它设置它setXxx(),
-
public int getXxx(){ //获取值是通过返回值,所以有返回值类型,没有参数 return a; } public void setXxx(int a){ //set是设置值,需要接受外边传来的值,不需要返回 a = 1; }
-
不对外暴露私有的方法
-
Java封装性的体现需要权限修饰符配合
-
修饰类的话只能使用缺省和public,内部类可以使用private
-
构造器:(创建对象Person p = new Person(),初始化对象属性)
-
构造器不属于方法,因为方法是可以被对象调用的,而构造器不能
继承
-
一旦子类A继承了父类B,A就获取了B的所有的属性、方法
特别的,父类声明为private的属性和方法,子类继承父类后,仍然获得了父类中私有的结构,只是因为封装性的影响,使得子类不能直接调用父类的结构而已。
-
父类中的属性声明为private时,使用这个属性时,其实是有它,只是想要调用属性,只能使用setXxx()或getXXX()
-
一个类可以被多个子类继承,但一个类只能继承一个父类
-
所有的类都直接或间接继承object类,就算只继承了B,但是父类B继承了Object类
多态:
-
子类Man必须继承父类Person,而且必须重写父类中的部分方法
-
多种形态,Person p = new Man();
-
编译期只能调用父类声明的方法,不能调用子类中的方法(在子类中定义的方法,并不能被使用,但其实内部时加载的有),在运行期输出的是子类中重写过的数据
-
编译看左边,运行看右边
-
只适用于方法,不适用于属性
-
想要运行子类中定义的方法,得强制类型转化
public class Main {
public static void main(String[] args) {
Person p =new Person();
p.eat(); //人吃
p.walk(); //人走
Man m = new Man();
m.eat(); //男人吃
m.walk(); //男人走
m.play(); //男人玩
//多态体现1
Person p1 = new Man(); //内部其实加载了类Man中的play()方法,只是现在不能被调用
Man man =(Man) p1; //想要使用子类中自己的方法,就的强制类型转换
p1.walk(); //男人走
p1.eat(); //男人吃
int age = p1.age; //调用的是Person中的变量
System.out.println(age); //10
//p1.play(); //不能被调用
//p1只能使用父类中
System.out.println("***********");
//多态体现2,如果想直接调用子类方法
Main main = new Main();
main.fun(new Person());
main.fun(new Man());
main.fun(new Women());
//多态体现2
}
public void fun(Person p){ //Person p = new XXX();
p.eat();
p.walk();
}
//多态体现2
//如果没有多态性,就得重新写一个重载的方法
// public void fun(Man man){
// man.eat();
// man.walk();
// }
}
class Person {
int age = 10;
public void eat(){
System.out.println("人吃");
}
public void walk(){
System.out.println("人走");
}
}
class Man extends Person{
int age = 20;
public void eat(){
System.out.println("男人吃");
}
public void walk(){
System.out.println("男人走");
}
public void play(){
System.out.println("男人玩");
}
}
class Women extends Person{
public void eat(){
System.out.println("女人吃");
}
public void walk(){
System.out.println("nv人走");
}
public void play(){
System.out.println("女人玩");
}
}
向下转型:
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法,但是由于声明的变量类型为父类,导致编译时只能调用父类中的方法,子类特有的方法和属性不能调用,如何使用子类中的方法,使用向下转型
目的就是想比较子类对象中自己的属性,就是说重写equals()方法
public boolean equals(Object obj) {
if (this == obj){
return true;
}
if (obj instanceof Person){ //判断是否是Person类
Person person = (Person) obj; //强制转换
return this.age == person.age && this.name.equals(person.name); //因为name是String类型
}
return false;
}
==和equals()方法的区别
-
==比较的是基本数据类型的时候比较的是值(不一定类型相同)
-
==比较的是引用数据类型的,比较两个对象的地址值
-
equals()方法只能使用在引用数据类型中,不能使用在基本数据类型中,可以使用在包装类型中。
-
Object类中equals()和==相同
-
像String,Date,包装类等都重写了equals()方法,比较的不是两个引用地址是否相同,而是比较的值
-
Person p =new Person(); Person p2 =new Person(); System.out.println(p.equals(p2)); //false,因为这个是继承自父类object中的equals()方法,比较的是地址值 String s = new String("小明"); String s1 = new String("小明"); System.out.println(s.equals(s1)); //true,因为重写了类object中的equals()方法,比较的是值
-
重写equals()方法
-
obj instanceof Person这个obj可以是子类或者本身类
-
public class Main { public static void main(String[] args) { Person p =new Person(12,"小黄"); Person p2 =new Person(12,"小黄"); System.out.println(p.equals(p2)); //truee,因为这个是继承自父类object中的equals()方法,比较的是地址值 String s = new String("小明"); String s1 = new String("小明"); System.out.println(s.equals(s1)); //true,因为重写了类object中的equals()方法,比较的是值 } } class Person { int age; String name; public Person() { } public Person(int age, String name) { this.age = age; this.name = name; } public boolean equals(Object obj) { if (this == obj){ return true; } if (obj instanceof Person){ //判断是否是Person类, Person person = (Person) obj; return this.age == person.age && this.name.equals(person.name); //因为name是String类型 } return false; } }
toString()
- 自定义的类没有哦重写toString(),所以输出的是地址
- String,Date,File重写过toString()方法,所以比较的是值
System.out.println(p2.toString()); //org.example.Person@1b6d3586 地址值因为没有重写toString方法,
System.out.println(s.toString()); //小明 因为String重写了toString()方法
包装类
Integer i = new Integer(1);
Integer i2 = new Integer(1);
System.out.println(i == i2); //false
Integer m = 1;
Integer n = 1;
System.out.println(m == n); //true
Integer x = 128;
Integer y = 128;
System.out.println(x == y); //false
//因为Integer内部封装了一个缓存的东西,把-128~127之间的数放入,
//大于127的就相当于new Integer()
Integer x = 128;
Integer y = 128;
System.out.println(x == y); //false
//因为Integer内部封装了一个缓存的东西,把-128~127之间的数放入,
//大于127的就相当于new Integer()
其他关键字:
-
this,super,static,final,abstract,interface,package,import
-
this可以调用属性,方法,调用构造器,必须在第一行写this();
-
super可以调用属性和方法(和this差不多,可以混合使用),调用构造器必须写在第一行且super和this只能出现一个,没有写就默认调用super()
static:
不论new多少个对象,某些特定的数据我们只需要内存中只有一份,只要修改一处,其余都会改变
static可以修饰成员变量,方法,代码块,内部类,不能用来修饰局部变量
类中属性分为静态变量和实例变量
静态变量随着类的加载而加载,放在方法区的静态域,高于对象的创建,由于类只会加载一次,所以,静态变量只有一份
- 静态变量:被static修饰,且该类和该类的子类都共享这一个变量,如果改变就全部改变,使用变量名.静态变量或者类.静态变量
- 实例变量:都独立的拥有一套自己的非静态属性,改变一个值的话,别的实例对象并不会被改变,只能使用变量.静态变量
public class Main {
public static void main(String[] args) {
Person p =new Person(12,"小黄");
Person p2 =new Person(12,"小黄");
Person1 person1 = new Person1();
person1.nation = "地球";
//p.nation = "地球人";
System.out.println(p.nation);//中国 地球
System.out.println(p2.nation);//中国 地球
System.out.println(Person.nation);//中国 地球
System.out.println(person1.nation);//中国 地球
}
}
class Person {
int age;
String name;
static String nation = "中国";
public Person() {
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
}
class Person1 extends Person{
int age;
String name;
public Person1() {
}
public Person1(int age, String name) {
this.age = age;
this.name = name;
}
}
- 静态方法:被static修饰,内部只能调用静态方法和变量,静态方法随着类的加载而加载类.static或变量名.static,不能使用this和super
- 非静态方法:内部可以调用静态和非静态的方法和变量
private int id;
private static int init = 1001;
public Person() {
id = init++;
}
public Person(int age, String name) {
this.age = age;
this.name = name;
id = init++;
}
//调用无参的方法创建对象
Person p =new Person();
Person p2 =new Person();
System.out.println(p.getId()); //1001
System.out.println(p2.getId()); //1002
//调用有参的方法创建对象
Person p =new Person(12,"小明");
Person p2 =new Person(12,"小明");
System.out.println(p.getId()); //1001
System.out.println(p2.getId()); //1002
代码块:
- 代码块用来 初始化类、对象,如果修饰只能使用static
- 静态代码块:
- 内部可以有输出语句
- 随着类的加载而执行,且只能执行一次。
- 作用:初始化类的信息
- 如果一个类中定义了多个静态代码块,按声明顺序执行
- 可以放静态变量,静态方法,不能存放成员变量,因为成员变量随着对象的加载而加载,这时候还没有成员变量
- 非静态代码块:
- 内部可以有输出语句
- 随着对象的创建而执行
- 每创建一个对象,就执行一次非静态代码块
- 作用:可以在创建对象是,对对象进行初始化
- 如果一个类中定义了多个非静态代码块,按声明顺序执行
对属性赋值的位置:
①默认初始化
②显示初始化/在代码块中赋值
③构造器初始化
④对象.属性或对象.方法
final:
- final可以修饰类,方法、变量
- final修饰类时,此类不能被继承
- final修饰方法,子类就不能取重写该方法
- final修饰变量:此时,变量就是一个常量,修饰基本数据类型的变量,值不能被改变,修饰引用数据类型的变量表示地址不能被改变
static 和 final 能一起修饰属性和方法
抽象:
abstract可以修饰类,方法。
abstract修饰类:抽象类
- 此类不能被实例化
- 抽象类中一定有构造方法,以便子类去实例化,默认不写
abstract修饰方法:抽象方法:
- 抽象方法只是方法的声明,没有方法体
- 包含抽象方法的类,一定是抽象类,抽象类可以不包含抽象方法
- 若子类重写了父类的所有抽象方法,此子类可实例化
- 若子类没有重写父类的抽象方法,则子类也是抽象类,使用abstract修饰
abstract不能用来修饰:属性,构造器,私有方法,静态方法,final方法,final类
接口:
接口使用interface定义,接口只能继承接口,可以多继承接口
类和接口是并列的两个结构
如何定义接口:
7之前默认不写是public static final 和public abstract
8默认不写是public static final 和public,因为还乐意为static 和 default
- JDK7及以前:只能定义抽象方法,和全局常量
- 全局常量:public static final的,但是书写时,可以省略不写
- 抽象方法:public abstract的,可以省略
- JDK8,除了全局常量和抽象方法。还可以定义静态方法、默认方法(default)
- 接口中定义的静态方法只能通过类.方法去调用
- 通过实现类的对象去调用默认方法
- 如果子类(实现类)继承的父类和接口中有同名同参数的方法,子类在没有重写的前提下,会调用父类的方法
- 如果实现类实现了多个接口,且接口中有同名同参数的方法,程序报错,必须重写实现类中的方法
- public default void a(){} 正确的
- public void a(){ } 错误的
接口中不能定义构造器,意味着接口不能被实例化 ----------------- 抽象类中一定有构造方法
Java开发中,接口通过让类去实现(implements)的方式来使用
- 如果实现类实现了接口的所有的方法,则此类可以被实例化
- 没有实现接口的所有方法,则还是抽象类
- 接口可以多继承接口
接口的具体使用,体现多态性
接口,实际上可以看做一种规范
继承可以不重写父类的方法,但是实现就必须重写所有的抽象方法
class Man extends Person implements Cookie,Work{
}
单例模式
所谓单例设计模式,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例。
饿汉式 坏处:对象加载时间过长 好处:线程安全
懒汉式 坏处:线程不安全 好处:延迟对象的创建
饿汉式
public class Main {
public static void main(String[] args) {
//用类.属性去调用
Person p = Person.getInstance();
Person p2 = Person.getInstance();
System.out.println(p == p2);
}
}
class Person {
//1.私有化构造方法
private Person(){
}
//内部实例化一个类,因为要用类.属性调用,所以static
private static Person instance = new Person();
//提供get方法去调用这个方法
public static Person getInstance() {
return instance;
}
}
懒汉式
public class Main {
public static void main(String[] args) {
//用类.属性去调用
Person p = Person.getInstance();
Person p2 = Person.getInstance();
System.out.println(p == p2);
}
}
class Person {
//1.私有化构造方法,为了不让外部去new对象
private Person(){
}
//属性设置为null,因为要用类.属性调用,所以static
private static Person instance = null;
//提供get方法去调用这个方法
public static Person getInstance() {
//判断调用getInstance()方法时,得到的是不是同一个
if (instance == null){ //==null就说明之前没创建,可以实例化一个
instance = new Person();
}
return instance; //说明之前new过
}
}
public class singleTest {
public static void main(String[] args) {
Bank b = Bank.getB();
Bank b1 = Bank.getB();
System.out.println(b == b1);
}
}
class Bank{
private Bank(){
}
private static Bank b = null;
public static Bank getB(){
if (b == null) {
synchronized (Bank.class) {
if (b == null) {
b = new Bank();
}
}
}
return b;
}
}
异常:(Throwable)
Error:
- java虚拟机无法解决的问题。
Exception:
-
编译时异常
- IOException
- FileNotFoundException
- IOException
-
运行时异常
-
NullPointerException
-
int a[][] = new int[3][]; a[0][1]
-
ArrayIndexOutOfBoundsException
-
int a[] = new int[3] a[3]
-
ClassCastException (类不一样)
-
Object obj = new Date(); String str = (Obj)obj
-
NumberFormatException(数值转换)
-
String a = "abc"; Integer b = Integet.parseInt(a);
-
InputMismatchException(输入不匹配,输入数字,却输入字符)
-
Scanner sc = new Scanner(System.in); int score = sc.nextInt(); sout
-
ArithmeticException
-
int a = 0; int b = 1; b/a;
-
异常的两种处理方式
try{ }catch(异常类型1 异常变量1){ }finally{ }
-
finally是可选的
-
使用try将可能出现异常的代码包装起来,在执行过程中一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch去匹配
-
一旦try中异常对象和catch()中一致,就进入catch进行处理,处理完成,跳出当前try -catch结构,在没有写finally的情况下。继续执行之后的代码
-
catch中的异常如果没有子父类关系,和顺序无关,如果有关系,子类应定义在父类前边,否则,报错
-
常用的异常对象处理方式①String getMessage()②printStackTrace()
-
在try中定义的出了try就不能被调用,在外部初始化赋值
-
finally表示这一段代码必须被执行,就像资源的关闭,就是说jvm垃圾处理机制不执行的代码,
throws + 异常类型
- throws + 异常类型 写在方法的声明处,指明可能抛出的异常
- 一旦方法执行后,出现异常,判断是否和throws后的异常类型一致,一致就抛出,
- 异常后续的代码不再执行
- throws方式,把异常交给了调用者,调用者还要去处理
手动异常throw
throw一个异常对象,并抛出,这个异常之后的代码就不执行了,
throw new Exception();
如何自定义异常类:
- 创建一个类去继承RuntimeException或Exception类
- 提供全局变量:serialVersionUiD
- 提供重载的构造器
throw和throws:
- 一个是手动生成异常并抛出,一个是处理异常的方式之一
- throw在方法体内,throws在方法后
线程:
第一种:
存在线程安全问题
- 创建一个类去继承Thread类。
- 重写run()方法,在run()方法中写出程序,不能直接调用run()方法,要不然就是单线程,怎么证明?Thread.currentThread().getName()
- 在主类中创建这个类的实例
- 调用start()方法①启动当前线程②调用当前线程的run()
- 一个对象只能调用一次start()方法,会报错,需要重新创建一个线程对象
public class Main{
public static void main(String[] args) {
//3.创建子类对象
Person person = new Person();
//4.调用start()
person.start();
//person.run();
for (int i = 0; i < 100; i++) {
if (i%2==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
//1.创建一个类去继承Tread类
class Person extends Thread{
//2.重写run()
@Override
public void run(){
for (int i = 0; i < 100; i++) {
if (i%2==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
Thread中常用方法:
- start(),启动当前线程;调用当前线程的run()方法
- run(),子类需要重写Thread类的此方法,将要操作的代码写在其中
- currentThread(),静态方法,返回当前线程
- getName(),获取当前线程的名字
- setName(),设置当前线程名字
- yield(),静态方法,释放当前cpu的执行权
- join(),在线程a中调用线程b的join(),此时线程a进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态。
- sleep(毫秒),执行到这时,阻塞多少毫秒,
- isAlive(),判断当前线程是否存活
线程的优先级:
- MAX_PRIORITY : 10
- MIN_PRIORITY:1
- NORM_PRIORITY:5 ---->默认都是5
- getPriority():获取当前线程的优先级
- setPriority():设置线程优先级
- 高优先级会抢占低优先级的cpu执行权,只是从概率上讲可能大一点,并不一定会真的抢占。
第二种:
存在线程安全问题
- 创建一个类去实现Runable接口
- 实现run()方法
- 在主类中创建子类对象
- 在主类中创建Thread类的对象,并将子类对象放入其中
- 调用Thread的start()方法
public class Main{
public static void main(String[] args) throws InterruptedException {
Windows windows = new Windows();
Thread thread = new Thread(windows);
Thread thread1 = new Thread(windows);
Thread thread2 = new Thread(windows);
thread.setName("窗口1");
thread1.setName("窗口2");
thread2.setName("窗口3");
thread.start();
thread1.start();
thread2.start();
}
}
//1.创建一个类去实现Runable接口
class Windows implements Runnable{
//2.实现run()方法
private int ticket = 100;
@Override
public void run() {
while (true){
if (ticket<0)
break;
System.out.println(Thread.currentThread().getName()+":"+ticket);
ticket--;
}
}
}
两种方式对比:
-
实现的方式比较好,因为继承只能单继承,实现可以实现多个接口
-
实现可以处理多个线程共享数据的问题
-
Thread类也实现了Runable接口
程序:一段静态的代码
进程:正在运行的程序
线程:进程的分支
Java中,我们通过同步机制,来解决线程安全问题
同步代码块:
- synchronized(同步监视器、锁){ //需要被同步的代码 } 就是将操作共享数据的代码包起来,
- 锁,就是任何类的对象,类也是对象。锁必须保持唯一性,不能创建多个对象
- 在实现Runable接口的方式中,可以使用this作为锁
- 在继承Thread类的方式中,必须保持创建对象的唯一性,要加static,不能使用this,可以考虑使用当前类去充当
public class Window {
public static void main(String[] args) {
W w = new W();
W w1 = new W();
W w2 = new W();
w.start();
w1.start();
w2.start();
}
}
class W extends Thread{
private static int ticket = 100;
static Object o = new Object(); //因为这个是new了三个对象,所以new了三个o,一定要保证锁的唯一
@Override
public void run() {
while (true) {
synchronized (o) {
if (ticket <= 0)
break;
System.out.println(currentThread().getName() + ":" + ticket);
ticket--;
}
}
}
}
同步方法:
- 创建一个方法,把要同步的代码放入其中,并使用synchronized修饰
- 同步方法仍然涉及到同步监视器,只是不需要显示声明
- 在实现Runable接口中,默认的是this,非静态是this
- 在继承Thread类中,默认的是当前类本身,静态是当前类本身
Lock锁:
- 在类中实例化ReentrantLock,使用try{ lock.lock }finally{ lock.unlock }
public class Windows3 {
public static void main(String[] args) {
Window7 window7 = new Window7();
Thread thread = new Thread(window7);
Thread thread1 = new Thread(window7);
Thread thread2 = new Thread(window7);
thread.start();
thread1.start();
thread2.start();
}
}
class Window7 implements Runnable{
private int ticket = 100;
private ReentrantLock reentrantLock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
reentrantLock.lock();
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
} else {
break;
}
}finally {
reentrantLock.unlock();
}
}
}
}
synchronized和ReentrantLock的区别:
相同:都可以解决同步问题
不同:synchronized机制在执行完相应的同步代码以后,自动释放同步监视器
ReentrantLock需要手动的启动同步(lock()),同步结束也需要手动关闭(unlock());
/**
* @author ssp
* @date 2021年07月15日 21:54
*银行有一账户,
* 有两个储户分别向同一个账户存3000元,每次存1000,存3次,每次存完打印账户余额
*
*/
public class BankTest {
public static void main(String[] args) {
Account account = new Account();
Person p = new Person(account);
Person p1 = new Person(account);
p.setName("甲");
p1.setName("乙");
p.start();
p1.start();
}
}
class Account{
private double balance = 0;
private static ReentrantLock lock = new ReentrantLock();//设置static为了共享锁
public void show(double a){
try {
lock.lock();
if (balance>=0){
balance += a ;
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"存入成功,余额为:"+balance);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
class Person extends Thread{
private Account account; //为了获取Account对象,然后调用构造方法时,直接传过去
public Person(Account account){
this.account = account;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
account.show(1000);
}
}
}
线程通信三个方法:
wait():一旦执行到该方法,当前线程就会阻塞,释放锁
notify():一旦执行此方法就会唤醒被wait()的一个线程,如果有多个就会唤醒高线程
notifyAll():一旦执行此方法就会唤醒所有被wait()的线程
三个方法都得在同步代码块或同步方法中
都定义在Object中
sleep()和wait()方法:
- 相同点:都使当前线程进入阻塞状态
- 不同点:sleep()定义在Thread中,wait()定义在Object中
- sleep()可以在任何场景调用,wait()只能在同步代码块和同步方法中
- 如果都在同步代码块或同步锁中,sleep()不会释放锁,wait()会释放锁
第三种:
- 定义一个类去实现Callable接口。
- 重写call()方法。
- 实例化这个子类
- 实例化FutureTask,将子类放入其中
- 创建一个线程,将FutureTask的实例化放入其中,
- 调用start()方法
- 使用FutureTask的get方法得到call()方法的值,使用Object类型接收
//①定义一个类去实现Callable接口
class NumberThread implements Callable {
int sum = 0;
//②重写call()方法
@Override
public synchronized Object call() throws Exception {
for (int i = 0; i <= 100; i++) {
if (i % 2 == 0){
sum += i;
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
return sum;
}
}
public class NumberTest {
public static void main(String[] args) {
//③实例化这个类
NumberThread thread = new NumberThread();
//④实例化FutureTask类,将子类的对象放入其中
FutureTask task = new FutureTask(thread);
//⑤创建一个新的线程去放入FutureTask的实例化对象,调用start()方法
Thread t1 = new Thread(task);
t1.start();
try {
//⑥调用FutureTask的get方法的到子类的输出,用Object接受
Object a = task.get();
System.out.println(a);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
如何理解实现Callable接口比实现Runable接口更好
- call()方法有返回值
- call()方法可以抛出异常,被外面捕获,获取异常信息
- Callable支持泛型
第四种:
使用线程池好处
1.提高响应速度(减少了创建新线程的时间)
2.降低资源消耗(重复利用线程池中线程,不需要每次创建)
3.便于线程管理
-
提供指定数量的线程池
-
ExecutorService service = Executors.newFixedThreadPool(10);
class Run implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
public class PoolTest {
public static void main(String[] args) {
//提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//执行指定线程的操作,需要提供实现Runable接口和Callable接口的对象
//service.execute(new Run());//适合Runable
service.submit(new Run());//适合Runable和Callable
service.shutdown();//关闭连接池
}
}
String:
String,StringBuffer,StringBuild三者异同?
String是不可变序列,底层使用char[]
StringBuffer是可变序列:线程安全,效率低,底层使用char[]
StringBuild是可变序列,线程不安全,效率高,底层使用char[]
String–>StringBuffer,StringBuild :调用构造器
StringBuffer stringBuffer = new StringBuffer("abc");
String s = new String(stringBuffer);
StringBuffer,StringBuild–>String:调用构造器,调用·toString()
String s1 = "abc";
StringBuffer stringBuffer1 = new StringBuffer(s1);
- String声明为final的,不可被继承
- String实现了Serializable接口:表示可序列化的,实现了Comparable接口,表示可以比较大小
- String内部定义了final char[] value用于存储字符串数据
- String s = “abc”,这种方式定义一个字符串存储在常量池中,常量池中是不会存储两个相同的字符串
- 重新赋值后,会重新生成新的地址
- 对现有的字符串进行连接操作时,也会生成新的地址
- 当调用replace()方法进行字符串修改时,也需要重新指定内存地址
String s = "abc";
String s1 = "123";
String s2 = "abc123";
String s3 = "abc"+"123";
String s4 = s + "123";
String s5 = "abc" + s1;
String s6 = s4.intern();
System.out.println(s2 == s3); //true
System.out.println(s2 == s4);//false
System.out.println(s3 == s4);//false
System.out.println(s2 == s5);//false
System.out.println(s2 == s6);//true
如果是直接赋值的字符串和拼接的字符串,他们的数据相同
-
常量与常量的拼接结果在常量池,且常量池终不会存在相同的常量
-
只要其中有一个变量,结果就在堆中
-
如果拼接的结果调用intern()方法,返回值就在常量池中
length():返回字符串的长度
charAt(int index):返回指定位置的字符
isEmpty():判断是否为空串
toLowerCase():将String中所有字符转换为小写
toUpperCase():将String中所有字符转换为大写
trim():去除字符串首尾的一个或多个字符串
equals():比较两个字符串的内容是否相同
equalsIgnoreCase():忽略大小写比较两个字符串的内容是否相同
concat():将指定字符串连接到此字符串结尾,相当于“+”
int compareTo(String anotherString):比较两个字符串的大小
- 如果内容的长度相等,比较的是大小。
String subString(int beginindex):获取从index开始朝后的字符串
String subString(int beginindex,int endindex):获取从beginindex开始到endindex之间的字符串(前报后不包)
endWith():判断是否以指定字符串结尾
startWith():判断是否以指定字符串开始
indexOf():返回指定字符串第一次出现的索引
valueOf():将包装类转换为String
replace(char old,char new):返回一个被new替换的新的字符串
split(),按指定字符切分
String -->char[] :调用String的toCharArray()方法
char[]–>String,调用String的构造器
StringBuffer:
-
可变的字符序列,线程安全,但是执行效率低
String s = new String(); //char[] value = new char[0]; String s1 = new String("abc");//char[] value = new char[]{'a','b','c'} StringBuffer sb1 = new StringBuffer(); //char[] value = new char[16];底层创建了一个长度为16的字符集合 System.out.println(sb1.length()); //0 sb1.append('a'); //value[0] = 'a'; sb1.append('b'); //value[1] = 'b'; System.out.println(sb1); //ab StringBuffer sb2 = new StringBuffer("abc");//char[] value = new char["abc".length() + 16] System.out.println(sb2.length()); //3
提前就会空出来16位
如果要添加的数组底层数组盛不下,就扩容数组
默认情况,扩容为原来容量的两倍+2,同时将原有数组中的元素复制到新的数组中
append():添加字符串
delete(int start,int end):删除指定位置的字符
replace(int start,int end,String str):更换指定位置的字符
insert(int offset,XXX),在指定位置插入XXX,不是替换
reverse():反转字符串
Date:
一个在util下,一个在sql下
System.currentTimeMills():获取从1970.1.1日到今天的毫秒数
1.两个构造器
2.两个方法
- toString():显示当前日期
- getTime():获取毫秒数
Date date1 = new Date();
System.out.println(date1.toString());//当前日期
System.out.println(System.currentTimeMillis()); //当前毫秒数
System.out.println(date1); //当前日期
System.out.println(date1.getTime()); //当前毫秒数
Date date = new Date(1626432584889l); //创建指定毫秒数的日期
System.out.println(date);
SimpleDateFormate:
//定义一个自定义格式的年月日
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
//获取时间
Date date = new Date();
System.out.println(date);
//将时间转换成一个字符串
String s = simpleDateFormat.format(date);
System.out.println(s);
//定义一个时间格式,要求这个字符串必须是和定义的格式一致
String s1 = "2021-08-17 02:50:36";
//将这个时间格式转换成默认的
Date date1 = simpleDateFormat.parse(s1);
System.out.println(date1);
将java.util.Date转换为java.sql.Date
String s = "2021-06-16";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
//这个是java.util.Date
Date date = dateFormat.parse(s);
System.out.println(date);
//调用构造器转换,date.getTime()
java.util.Date date1 = new java.sql.Date(date.getTime());
System.out.println(date1);
Calendar日历类是个抽象类
**Calendar.getInstance();**调用
LocalDate,LocalTime,LocalDateTime:
都是抽象类
实例化:调用静态方法。now()和of()
LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDate);
System.out.println(localTime);
System.out.println(localDateTime);
LocalDateTime localDateTime1 = LocalDateTime.of(2021,10,22,4,50,30);
System.out.println(localDateTime1);
Instant:瞬时
now()方法去实例
Comparable:
Comparable接口的使用:(自然排序)
- 因为String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,所以定义为String和包装类的对象数组能比较
- 对于自定义列来说,如果需要排序就实现Comparable接口,重写CompareTo()方法,指明如何排序。
Comparator:
- 实现compare(object a,object b)方法比较
区别:Comparable接口的方式一旦确定,保证Comparable接口实现类对象在任何位置比较大小都可以。Comparator接口,属于临时性比较。
枚举类:
类的对象只有有限个,确定的。比如:季节,只有春夏秋冬。星期:(1-7)
当需要定义一组常量时,强烈建议使用枚举。若枚举类只有一个对象,则可以作为单例模式实现方式
-
在5.0之前,要自定义枚举类
-
可以使用enum关键字来定义
-
public class enumTest { public static void main(String[] args) { //直接使用类。对象,默认使用public static final Season season = Season.SPRING; //定义的枚举类默认继承与java,lang。Enum类 System.out.println(season);//SPRING } } enum Season{ //1.首先提供当前枚举类的对象,用“,”隔开,最后使用“;” SPRING("春天","春天来了"), SUMMER("夏天","夏天来了"); //2.声明Season的属性 private final String seasonName; private final String seasonDesc; //3.私有化构造器,并给属性赋值 private Season(String seasonName,String seasonDesc){ this.seasonName = seasonName; this.seasonDesc = seasonDesc; } public String getSeasonName(){ return seasonName; } public String getSeasonDesc(){ return seasonDesc; } }
-
toString():返回当前对象的名称
-
values():返回当前枚举类的所有对象
-
valueOf(String objName):返回对象名是objName的对象
-
集合:
集合,数组都是对多个数据进行存储操作的结构,简称Java容器
数组在存储数据的特点:
- 一旦初始化,长度就确定了。
- 数组一旦定义好,其元素的类型也就确定了,我们只能操作指定类型的数据,可以存储基本数据类型和对象,集合只能存储对象类型
数组在存储数据的缺点:
- 一旦初始化长度就不可以改变
- 数组提供的方法非常有限,对于增删改查等操作,不方便,效率不高
- 获取数组中实际元素的个数,数组没有方法去获得
- 数组存储数据的特点:有序,可重复。
Java集合可分为Collection和Map体系:
- Collection接口:单列数据
- List:元素有序,可重复
- set:元素无序,不可重复
- Map接口:双列集合,key - value对集合
Collection接口的方法:
add(object)添加元素
addAll(Collection c) 将其他集合中的元素,添加到其中
size()方法 查看有多少个元素
toString() 输出集合元素
remove() 删除指定名称的元素
removeAll():删除集合1中所有和集合2相同的数据
clear() 清空集合元素
isEmpty() 判断集合元素是否为空
contains(Object o): 内部使用equals()进行判断,因为String重写了,所以是值对比,如果是自定义对象,且没有重写equals方法,比较的就是地址
containsAll(Collection c):判断集合1中是否包含集合2
retainAll(); 求集合1和集合2的交集
equals():比较两个集合是否相同
toArray():集合转化为数组
Iterator:集合遍历
hasNext():判断是否有下一个元素,从第一个元素的前边开始,
next():指针下移,将下移以后的结合位置上元素返回
Iterator iterator = coll.iterator();
while (iterator.hasNext()){
Object o = iterator.next();
if ("123".equals(iterator)){
iterator.remove();
}
System.out.println(iterator.next());
}
遍历一遍之后,不能再用同一个iterator去遍历,因为这是指针已经在末尾了,需要重新获得一个迭代器对象
增强for循环:
遍历对象元素,内部使用迭代器
for(Object o : 对象){
}
ArrayList,LinkedList,Vector的区别:
都是实现了List接口,存储数据特点相同:有序的,可重复的
- ArrayList:作为List接口的主要实现类,线程不安全,效率高,对于查询操作,效率高,底层使用数组存储
- LinkedList:对于频繁的插入,删除操作,使用次类效率高,底层使用双向链表
- Vector:线程安全,效率低,底层使用数组存储
ArrayList源码分析:
jdk7:
-
ArrayList list = new ArrayList()//底层创建了长度为10的object[]数组。
-
如果add()超过第10个,数组就会进行扩容,默认情况下,扩容为原来的1.5倍,同时将原本数组copyof()到新数组中
-
ArrayList list = new ArrayList(int capacity),使用带参的构造器创建指定长度的数组
jdk8:
-
ArrayList list = new ArrayList(), //底层创建了的object[]数组,初始化为{},并没有创建长度。
-
add(),第一次add()操作时,底层才创建了一个为10的数组
LinkedList:
- LinkedList list = newLinkedList(),内部声明了Node类型的first和last,属性默认是null,
- add(),将add的属性封装到Node中,创建了Node对象
Vector:
- 扩容为两倍
List常用方法:
-
add(int index,Object o):在指定位置插入元素
-
addAll(int index,Collection c):在指定位置插入集合
-
get(int index):得到某个位置的元素
-
indexof(Object o):返回指定元素在第一个出现的索引
-
lastIndexOf():返回指定元素在最后一个出现的索引
-
remove(int index ):删除指定位置的元素
-
set(int index,Object o):设置指定index位置的元素
-
List subList(int formIndex,int toIndex):返回两个区间之内的元素
Set:
HashSet:作为Set接口的主实现类,线程不安全,可以为null,底层数组+链表,初始容量为16,扩容为2倍
**LinkedHashSet:**作为HashSet的子类;遍历内部数据时,按照添加的顺序遍历
**TreeSet:**可以按照添加对象的指定属性,进行排序。
**无序:**不等于随机性。存储的数据在数组中按照哈希值去排序
**不可重复性:**保证添加的元素按照equals()判断时,不能反悔true,即相同的元素只能添加一个,根据hashCode()去判别
添加元素的过程:以HashSet为例:
我们向HashSet中添加元素a,首先调用元素a的hashCode()方法,计算元素a的哈希值,次哈希值通过某种算法计算出在HashSet底层数组中存放的位置,如果此位置上没有值,则添加成功,如果有元素b(或以链表形式存在的多个元素),则比较元素a和元素b的哈希值,如果不相同,则成功,如果相同,则需要调用a的equals()方法,如果返回值为true则不成功,为false则成功。如果这个位置有元素,则就以链表的形式存储。
jdk7:新添加的元素在数组中,指向原来的元素
jdk8:原来的元素在数组中。指向元素a
向Set中添加数据,其所在的类一定要重写hashCode()和equals()
TreeSet中添加数据时,必须是同种类型的。可以进行排序,排序方法为自然排序和定制排序,红黑树
自然排序中,比较两个对象是否相同的标准为compareTo()返回值,不在是equals()
Map:
- HashMap:作为Map的主实现类,线程不安全,效率高,可以对key和value存储null
- LInkHashMap:保证在遍历Map元素时,可以按照添加的顺序实现遍历,在原有的HashMap底层中,添加了指针,对于频繁的遍历操作,执行效率高于HashMap
- TreeMap:保证按照添加key-value对进行排序,实现排序遍历,此时考虑key的自然排序和定制排序,底层使用红黑树
- Hashtable:作为1.0的实现类,线程安全,效率低,不能对key和value存储null
- Properties:常用来处理配置文件。key和value都是String类型
Map结构:
- Map中的Key:无序的,不可重复的,使用Set存储所有的Key,key所在类必须要重写HashCode和equals方法。
- Map中的Value:无序的,可重复的,使用Collection存储所有的value,value所在类要重写equals()方法
- 一个键值对:key-value构成了一个Entry对象
- Map中到的entry:无序的,不可重复的,使用Set存储所有的Entry
HashMap底层实现原理:
jdk7:
HashMap map = new HashMap():调用空参构造器,底层创建了长度为16的一维数组Entry[] table。
map.put(key,value)
首先调用key所在类的HashCode()计算key的哈希值,此哈希值经过某种算法后,得到在Entry的存放位置。如果此位置没有元素,则存放成功。如果有元素(意味着存在一个或多个数据(以链表的形式存储)),比较key和已经存在的数据的哈希值,如果哈希值不相同,则存放成功,如果和此位置上的任意一个相同,则比较equals()方法,返回true,则使用value替换已经存在的value,返回false,则添加成功。
默认扩容为原来的2倍,当超过临界值(且该数据存放的位置为空)并将原有数据copyof过来
jdk8相较于jdk7的不同:
- new HashMap():底层没有创建一个长度为16的数组
- 8底层使用的Node[] ,而非Entry[]
- 首次调用put方法时,生成16位的数组
- jdk7底层结构只有数组和链表 ,8中有数组+链表+红黑树
- 当数组的某一个索引位置上的元素以链表存在的数据个数 > 8且当前数组的1长度 > 64时,此时该索引位置使用红黑树存储。
- jdk7的时候,新的元素指向旧的元素,8,旧的元素指向新的元素
默认容量:16
default_LoAD_FACTOR:默认加载因子:0.75。决定了HashMap的密度
扩容的临界值:容量 * 扩容因子 = 16 * 0.75 =12
map.keySet():返回Set集合的Key,使用iterator遍历
map.values(): 返回value,
map.entrySet():返回key-value值,
TreeMap:
向TreeMap中添加key-value,要求Key必须有同一个类创建的对象。自然排序和定制排序
HashMap map = new HashMap();
map.put("小黄",123);
map.put("小蓝",12);
map.put("小红",13);
map.put("小绿",23);
System.out.println(map);
// Set set = map.entrySet();
// Iterator iterator = set.iterator();
// while (iterator.hasNext()){
// Object next = iterator.next();
// Map.Entry entry = (Map.Entry) next;
// System.out.println(entry.getKey()+"-----"+entry.getValue());
// }
Set set = map.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
Object next = iterator.next();
Object o = map.get(next);
System.out.println(next+"---"+o);
}
ArrayList和HashMap都是线程不安全的,如果想要安全,使用Collections中的synchronizedList(List list)和synchronizedMap(Map map)
泛型:
要保证里边输出的数据,必须是同一种类型的。
在实例化集合类时,可以指明具体的泛型类型
在指明泛型以后,内部结构凡是使用到泛型,都必须声明为指定的类型
比如add(E e)—>add(Integer i)
泛型必须是类,不能是基本数据类型,如果是也是包装类,如果没有用泛型,默认Obejct
public class CollectionTest {
public static void main(String[] args) {
//指定为Integer类型,只能放Integer类型的值,泛型中不能使用基本数据类型
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(123);
list.add(456);
// list.add("123");//报错
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
Integer integer = iterator.next();
System.out.println(integer);
}
HashMap<String,Integer> map = new HashMap<String,Integer>();
map.put("小黄",123);
map.put("小黄",456);
map.put("小红",123);
//Entry为Map的内部接口,Set类型,里边是Entry值
Set<Map.Entry<String,Integer>> set = map.entrySet();
Iterator<Map.Entry<String,Integer>> iterator1 = set.iterator();
while (iterator1.hasNext()){
//Entry类型
Map.Entry<String,Integer> entry = iterator1.next();
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key+"----"+value);
}
}
自定义泛型:
在定义的类后边加上,在类中声明T person;
T person; //可以定义一个泛型的变量
如果new的对象没有使用泛型,则默认Object
如果限制了类型,则只能填入正确类型
子类继承了父类的泛型,如果为,则子类想要使用某一类型时,需自己声明
如果父类指明了类型,则生成的子类中继承了父类中的泛型,可以不写
File:
File类的对象代表一个文件或一个文件目录
File类声明在java.io
File类涉及到文件或文件夹的创建(mkdir,mkdirs,createNewFile),删除(delete),重命名(renameTo),长度(length()),存在(exists),
并未涉及到写入或读取文件的操作,那需要使用IO流
IO:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tlw7LcBI-1627206416799)(Java基础.assets/1626950505681.png)]
流的分类:
- 按数据流向:输入流,输出流
- 操作数据的单位:字节流,字符流
- 流的角色:节点流,处理流
对于文本文件(.txt.java.c.cpp),使用字符流,对于文本文件,只是复制操作时,不查看,可以使用字符流。
对于非文本文件(。jpg。avi。mp3,。doc),使用字符流
- 创建文件
- 创建节点流
- 操作流
- 关闭流
抽象基类 节点流(文件流) 缓冲流(处理流)
InputStream FileInputStream BufferInputStream
OutputStream FileOutputStream BufferOutputStream
Reader FileReader BufferReader
Writer FileWriter BufferWriter
File file = new File("src/main/java/org/day5/hello.txt");
FileReader fileReader = null;
try {
//1.创建FileReader对象
fileReader = new FileReader(file);
while (true){
//2.读取一个字符,不带参方法返回int类型,-1为最后
int data = fileReader.read();
if (data!=-1) {
//需要强转一下,否则就输出该字符的ASCLL码
System.out.print((char) data);
}else
break;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//一定记得关闭流
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
read():读取一个字符,不带参方法返回int类型,-1为最后
异常处理:使用try-catch-finally,因为要保证finally的执行
read(char[]): 读取指定位数的字符,返回读取数字
ileReader fileReader = null;
try {
//1.声明文件名称
File file = new File("src/main/java/org/day5/hello.txt");
//2.创建对象
fileReader = new FileReader(file);
int length = 0;
char[] c = new char[5];
//方式1:正确的
// while ((length = fileReader.read(c) )!= -1){
// for (int i=0; i<length; i++){
// System.out.print(c[i]);
// }
// }
while ((length = fileReader.read(c)) != - 1) {
String string = new String(c, 0, length);
System.out.print(string);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
输出操作,对应的File可以不存在,系统会自动创建文件
如果存在,流使用FileWriter(file,false)或FileWriter(file),则会覆盖原本文件的内容。如果使用FileWriter(file,true),则不会覆盖原有文件内容
/**
* 从hello中读取文件存到hello2中
*/
FileReader fr = null;
FileWriter fw = null;
try {
File srcfile = new File("src/main/java/org/day5/hello.txt");
File desfile = new File("src/main/java/org/day5/hello2.txt");
fr = new FileReader(srcfile);
fw = new FileWriter(desfile);
char[] c = new char[5];
int len;
while ((len = fr.read(c)) != - 1){
fw.write(c,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fr!=null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
缓冲流:提高流的读取、写入速度。原因:内部提供了一个缓冲区(8 * 1024)
public void Buffer() {
//1.创建文件
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
File srcfile = new File("src/main/java/org/day5/珂珂.jpg");
File destfile = new File("src/main/java/org/day5/珂珂2.jpg");
//创建节点流
FileInputStream fis = new FileInputStream(srcfile);
FileOutputStream fos = new FileOutputStream(destfile);
//创建缓冲流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//定义字节数组接受
byte[] b = new byte[5];
int len; //定义长度去接收读取的位数
while ((len = bis.read(b)) != -1){
bos.write(b,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭外层流,此时内层流也会关闭
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos!=null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
转换流:
字符类型:使用char[]
InputStreamReader:实现字节输入流到字符输入流的转换,
OutPutStreamWriter:实现字符输出流到字节输出流的转换
网络编程:
1、如何准确的定位网络上的一台或多台主机;定位主机上的特定应用
2、找到主机后如何可靠高效的进行数据传输
网络编程的两个要素:
- IP和端口号
- 提供网络协议
OSI:物理层-》数据链路层-》网络层-》传输层-》会话层-》表示层-》应用层
TCP/IP:物理层+数据链路层-》网络层-》传输层-》应用层
通信要素一:IP和端口号
IP:
- 唯一标识Internet上的计算机
- 在Java中使用InetAddress类代表IP
- IP分类,IPv4,IPv6。万维网和局域网(192.168.0.0)
- 域名。www.baidu.com
- 本地地址,137.0.0.1,对应着localhost
- 如何实例化InetAddress:getByName(String host),getLocalhost()
- 两个常用方法:getHostName(),getHostAddress()
端口号:
- 正在计算机上运行的进程
- 要求:不同的进程有不同的端口号
- 范围:被规定为16位整数,0~65535
端口号与IP地址组合得出一个网络套接字:Socket
TCP协议:
- 使用TCP前必须先建立TCP连接,形成传输数据通道
- 传输前,采用“三次握手”方式,点对点通信,可靠的
- TCP协议的两个应用进程:客户端,服务端
- 在连接中可进行大数据量的传输
- 传输完毕,需释放已建立的连接,效率低
UDP:
- 将数据、源、目的封装成包,不需要建立连接
- 每个数据包大小限制在64K
- 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
- 广播,直播
- 发送数据结束时,无序释放资源,开销小,速度快