JAVA基础
java 特性
封装:
- 封装指的是属性私有化,根据需要提供setter和getter方法来访问属性。即隐藏具体属性和实现细节,仅对外开放接口,控制程序中属性的访问级别。
- 目的:增强安全性和简化编程,使用者不必在意具体实现细节,而只是通过外部接口即可访问类的成员。
继承:
- 继承是指将多个相同的属性和方法提取出来,新建一个父类。
- Java中一个类只能继承一个父类,且只能继承访问权限非private的属性和方法。 子类可以重写父类中的方法,命名与父类中同名的属性。
- 目的:代码复用。
多态:
- 多态可以分为两种:设计时多态和运行时多态。
- 设计时多态:即重载,是指Java允许方法名相同而参数不同(返回值可以相同也可以不相同)。
- 运行时多态:即重写,是指Java运行根据调用该方法的类型决定调用哪个方法。
- 多态目的:增加代码的灵活度。
抽象:只公开与数据类型交互有意义的操作,对外界隐藏了所有实现细节
Java 三大版本
一次编写 到处运行 (jvm)跨平台
- javaSE:标准版(桌面程序)
- javaME:嵌入式开发(手机,家电)
- javaEE:企业级开发(web端,服务器开发)
JDK JRE JVM
JDK:Java Development Kit java开发工具包,用来开发Java程序的,针对java开发者
JRE:Java Runtime Environment java运行时环境,针对java用户
JVM:Java Virtual Machine java虚拟机 用来解释执行字节码文件(class文件)的
JDK包涵了JRE
JRE包涵了JVM
数据类型
强类型语言:要求变量的使用严格符合规定,所有变量都必须先定义后才能使用。
基本数据类型
基本数据类型只有四类八种
-
整数类型
-
byte 占用1个字节 1byte = 8bits 默认值为0
-
short 占用2个字节 1byte = 16bits 默认值为0
-
int 占用4个字节 1byte = 32bits 默认值为0
-
long 占用8个字节 1byte = 64bits 默认值为0L
-
boolean类型
-
占1位 true/false 默认值false
-
浮点型
-
float 单精度浮点型 占用4位 1float = 32 bits 默认值0.0f
-
double 单精度浮点型 占用8位 1float = 64 bits 默认值0.0d
-
字符类型
-
char 可以存储任何字符 例如 char a = 'A'
对应包装类:Byte Short Character Integer Long Boolean Double Float
数值类型 | 字节范围 |
---|---|
byte | 占1个字节 -128 —127 |
short | 占2个字节 -32768—32767 |
int | 占4个字节 -2147483648—2147483647 ±21亿 |
long | 占8个字节 -9223372036854775808—9223372036854775807 |
float | 占4个字节 |
double | 占8个字节 |
char | 占2个字节 |
boolean | 占1个字节 true/false |
进制问题:
public class demo {
public static void main(String[] args) {
//整数类型 二进制0b开头 十进制 八进制0开头 十六进制0x开头
int i = 10;
int i2 = 010; //8进制
int i3 = 0x10; //16进制 0-9 A-F
System.out.println(i);
System.out.println(i2);
System.out.println(i3);
//浮点数
//float 比较离散 有舍入误差 算出的值都是约等于 接近但不等于
//double
float f = 1.1111f;
double d = 1.1111;
System.out.println(f);
System.out.println(d);
System.out.println(f==d);//false
float fl = 1234567821341423412f;
float fl2 = fl+1;
System.out.println(fl==fl2);//true
//字符型
char c = 'A';
char c1 = '我';
System.out.println((int)c);//65
System.out.println((int)c1);//25105
//布尔类型
boolean flag = true;
if (flag == true){
}
if (flag){
}
}
}
10
8
16
false
true
引用数据类型
类
接口
数组
字节
-
位(bit):计算机内部存储数据的最小单位
-
字节(byte):是计算机中数据处理的基本单位,一般使用B表示
-
1B(byte,字节)= 8bit
-
字符:指计算机中使用的字母,数字,字和字符。
-
1bit表示1位
-
1Byte表示一个字节 1B = 8b
-
1024B = 1kb
-
1024kb = 1M
-
1024M = 1G
注:电脑中32位系统和64位系统
32位系统最大内存只能扩容到32gb 64位系统可支持128g
类型转换
强制转换:变量名 高->低
自动转换:低->高
- 不能对布尔值类型转换
- 不能吧对象类型转换为不相干类型
- 在把高容量转换低容量时候强制转换
- 强制转换可能存在内存溢出,或者精度丢失
变量
- 每一个变量必须声明其类型
- 类型可以是基本类型,也可以是引用类型
- 变量名必须是合法的标识符
- 变量名是一条完整语句,分号结尾
int i = 10;
//数据类型 变量名 = 值
变量的作用域
- 类变量 (加static关键字,类加载的时候就创建)
- 实例变量 (在对象中,可以不对其初始化值,有默认值)
- 局部变量 (在方法中,必须声明和初始化)
变量命名规范
- 所有变量,方法名,类名,见名知意
- 类成员变量:首字母小写和驼峰原则
- 局部变量:首字母小写和驼峰原则
- 常量:大写字母和下划线
- 类名:首字母大写和驼峰原则
- 方法名:首字母小写和驼峰原则
常量
- 初始化后不能改变再改变值
- 一般用大写
final 常量名 = 值
final String name = "w";
运算符
- 算数运算符:+,-,*,/,%,++,–
- 赋值运算符:=
- 关系运算符:>,<,>=,<=,==,!= instance of
- 逻辑运算符:&&,||,!
- 位运算符:&,|,^,~,>>,<<,>>>
- 条件运算符:?,:
- 扩展赋值运算符:+=,-=,*=,/=
public class Demo04 {
public static void main(String[] args) {
int a = 1;
int b = 1;
System.out.println(a++);//1 先赋值,后自增
System.out.println(--b);//0 先自增,后赋值
int c = 10;
int d = 3;
System.out.println(c%d);//取模 求余数
}
}
public class Demo05 {
public static void main(String[] args) {
//与 或 非
boolean a = true;
boolean b = false;
System.out.println(a&&b);//逻辑与运算:两个变量都为true,结果才为true
System.out.println(a||b);//逻辑或运算:两个变量有一个为true,结果就为true
System.out.println(!(a&&b));//取反:如果为真,则变为假 如果为假,则变为真
//短路与:如果左边为false,结果为false,不会运算后面等式
//短路或:如果左边为true,结果为true,不运算后边等式
}
}
public class Demo06 {
public static void main(String[] args) {
int a = 10;
int b = 20;
a+=b;//a=a+b
a-=b;//a=a-b;
System.out.println(a);
int c =10;
String aq = String.valueOf(c);
//字符串连接符
System.out.println(""+a+b);//1020 字符串在前转成string 会进行拼接
System.out.println(a+b+"");//30 字符串在后先相加 再转换成字符串
}
}
关键字
关键字 | 含义 |
---|---|
abstract | 表明类或者成员方法具有抽象属性 |
assert | 断言,用来进行程序调试 |
boolean | 基本数据类型之一,声明布尔类型的关键字 |
break | 提前跳出一个块 |
byte | 基本数据类型之一,字节类型 |
case | 用在switch语句之中,表示其中的一个分支 |
catch | 用在异常处理中,用来捕捉异常 |
char | 基本数据类型之一,字符类型 |
class | 声明一个类 |
const | 保留关键字,没有具体含义 |
continue | 回到一个块的开始处 跳过 |
default | 默认,例如,用在switch语句中,表明一个默认的分支。Java8 中也作用于声明接口函数的默认实现 |
do | 用在do-while循环结构中 |
double | 基本数据类型之一,双精度浮点数类型 |
else | 用在条件语句中,表明当条件不成立时的分支 |
enum | 枚举 |
extends | 表明一个类型是另一个类型的子类型。对于类,可以是另一个类或者抽象类;对于接口,可以是另一个接口 |
final | 用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变,用来定义常量 |
finally | 用于处理异常情况,用来声明一个基本肯定会被执行到的语句块 |
float | 基本数据类型之一,单精度浮点数类型 |
for | 一种循环结构的引导词 |
goto | 保留关键字,没有具体含义 |
if | 条件语句的引导词 |
implements | 表明一个类实现了给定的接口 |
import | 表明要访问指定的类或包 |
instanceof | 用来测试一个对象是否是指定类型的实例对象 |
int | 基本数据类型之一,整数类型 |
interface | 接口 |
long | 基本数据类型之一,长整数类型 |
native | 用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的 |
new | 用来创建新实例对象 |
package | 包 |
private | 一种访问控制方式:私用模式 |
protected | 一种访问控制方式:保护模式 |
public | 一种访问控制方式:共用模式 |
return | 从成员方法中返回数据 |
short | 基本数据类型之一,短整数类型 |
static | 表明具有静态属性 |
strictfp | 用来声明FP_strict(单精度或双精度浮点数)表达式遵循IEEE 754算术规范 |
super | 表明当前对象的父类型的引用或者父类型的构造方法 |
switch | 分支语句结构的引导词 |
synchronized | 表明一段代码需要同步执行 |
this | 指向当前实例对象的引用 |
throw | 抛出一个异常 |
throws | 声明在当前定义的成员方法中所有需要抛出的异常 |
transient | 声明不用序列化的成员域 |
try | 尝试一个可能抛出异常的程序块 |
void | 声明当前成员方法没有返回值 |
volatile | 表明两个或者多个变量必须同步地发生变化 |
while | 用在循环结构中 |
关键字一律用小写字母标识,按其用途划分为如下几组。
- 用于数据类型。 用于数据类型的关键字有 boolean、byte、char、 double、 false、float、int、long、new、short、true、void、instanceof。
- 用于语句。 用于语句的关键字有break、case、 catch、 continue、 default 、do、 else、 for、 if、return、switch、try、 while、 finally、 throw、this、 super。
- 用于修饰 用于修饰的关键字有 abstract、final、native、private、 protected、public、static、synchronized、transient、 volatile。
- 用于方法、类、接口、包和异常。 用于方法、类、接口、包和异常的关键字有 class、 extends、 implements、interface、 package、import、throws。
- 还有些关键字如cat、 future、 generic、innerr、 operator、 outer、rest、var等都是Java保留的没有意义的关键字。
- Java还有3个保留字:true、false、null。它们不是关键字,而是文字。包含Java定义的值。和关键字一样,它们也不可以作为标识符使用。
流程控制
1. Scanner
-
java.util.Scanner 类可以获取用户的输入
-
Scanner scanner = new Scanner(System.in);
-
通过Scanner类的next()与nextLine()方法获取输入的字符串,读取前使用hasNext和hasNextLine()判断是否还有输入的数据
Next
- 一定要读取到有效字符后才可以结束输入
- 对输入有效字符之前遇到的空格,next()方法会自动将其去掉
- 只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符
- next()不能得到带有空格的字符串
nextLine()
- 以Enter为结束符,也就是说返回的是输入回车之前的所有字符
- 可以获得空格
public class Demo01 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
if (scanner.hasNext()){
String s = scanner.next();//空格后面读取不到 以空格结束
System.out.println(s);
}
if (scanner.hasNextLine()){
String s1 = scanner.nextLine();//可以读取空格后面字符 以Enter结束
System.out.println(s1);
}
scanner.close();
}
}
public class Demo02 {
public static void main(String[] args) {
double sum = 0;//和
int num = 0;//个数
Scanner scanner = new Scanner(System.in);
while(scanner.hasNextDouble){
doouble d = scanner.nextDouble();
num++;
sum = sum + d;
}
System.out.println(sum);
System.out.println(num);
System.out.println(sum/num);//平均值
scanner.close();
}
}
2. if语句
//if单选择结构
if(){
}
//if双选择结构
if(){
}else{
}
//多选择结构
if(){
}else if(){
}else if(){
}else{
}
//if嵌套结构
if(){
if(){
}
}
3. Switch语句
- switch case语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。
- switch语句中变量的类型可以是byte,short,int,char
- jdk 7 开始支持string类型
- 同时case标签必须为字符串常量或字面量
- 每个case后带上break,防止case穿透
public class Demo03 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入");
String s = scanner.nextLine();
switch(s){
case "A":
System.out.println("a");
break;//加上break防止case穿透
case "B":
System.out.println("b");
break;
case "C":
System.out.println("c");
break;
default:
System.out.println("...");
}
scanner.close();
}
}
4. while 循环
while(布尔表达式){
//循环内容
}
- 只要布尔表达式为true,循环就会一直执行下去
- 大多数情况下会让循环停止下来,需要一个让表达式失效的方式结束循环
- 少部分情况需要循环一直执行,比如服务器的请求相应监听
- 循环条件为true就会造成无限循环,死循环,正常业务中需要避免,回影响程序性能
public class Demo04 {
public static void main(String[] args) {
//计算1+2+...+100
int i = 0;
int sum = 0;
while (i<100){
i++;
System.out.println(i);
sum = sum + i;
}
System.out.println(sum);
}
}
5. do while 循环
do{
//代码块
}while(布尔表达式);
- 对于do while而言,如果不满足条件,则不能进入循环
- do while 和while区别
- while先判断后执行,do…while先执行后判断
- do…while总是保证循环体至少执行一次
public class Demo05 {
public static void main(String[] args) {
int i = 0;
int sum = 0;
do {
i++;
sum = sum + i;
}while (i<100);
System.out.println(sum);
}
}
6. for循环
- for循环是支持迭代的一种通用结构
- for循环执行的次数是在执行前就确定的
for(初始化,布尔表达式,更新){
//代码块
}
for(int i = 0;i < 10;i++){
//代码块
}
计算0-100之间奇数和偶数的和
public class Demo06 {
public static void main(String[] args) {
//计算0-100之间奇数和偶数的和
int i = 0;
int jiSum = 0;
int ouSum = 0;
while (i<=100){
if (i%2==0){
ouSum = ouSum + i;
}else {
jiSum = jiSum + i;
}
i++;
}
System.out.println(jiSum);
System.out.println(ouSum);
}
}
public class Demo07 {
public static void main(String[] args) {
//计算0-100之间奇数和偶数的和
int jiSum = 0;
int oiSum = 0;
for (int i = 0; i <= 100; i++) {
if (i%2==0){
jiSum+=i;
}else {
oiSum+=i;
}
}
System.out.println(jiSum);
System.out.println(oiSum);
}
}
用while或for循环输出1-1000之间能被5整除的数,并且每行输出三个
public class Demo08 {
public static void main(String[] args) {
//用while或for循环输出1-1000之间能被5整除的数,并且每行输出三个
for (int i = 1; i <= 1000; i++) {
if (i%5==0){
System.out.print(i+"\t");
}
if (i%15==0){
System.out.println();
}
}
}
}
打印九九乘法表
public class Demo09 {
public static void main(String[] args) {
//打印九九乘法表
for (int j = 1; j <= 9; j++) {
for (int i = 1; i <= j; i++) {
System.out.print(j+"*"+i+"="+j*i+"\t");
}
System.out.println();
}
}
}
7. 增强for循环
for(声明语句:表达式){
//代码块
}
- 声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等
- 表达式:就是要访问的数组名,或者返回值为数组的方法
8. break continue
- break在任何循环体语句的主题部分,均可用break控制循环的流程。==break用于强行退出循环,==不执行循环体中剩余的语句。break语句也可以在switch语句中使用
public class breakDemo {
public static void main(String[] args) {
int i = 0;
while (i<10){
i++;
if (i==5){
break;
}
System.out.println(i);
}
}
}
- continue语句用在循环语句体中,用于跳过本次循环,继续进行下一次循环判断
public class continueDemo {
public static void main(String[] args) {
int i = 0;
while (i<100){
i++;
if (i%10==0){
continue;//跳过10,继续循环
}
System.out.print(i+" ");
}
}
}
9. 三种循环区别
- 三种循环的区别
- for循环和while循环先判断条件是否成立,然后决定是否执行循环体(先判断后执行)
- do…while循环先执行一次循环体,然后判断条件是否成立,是否继续执行循环体(先执行后判断)
- for循环和while的区别
- 条件控制语句所控制的自增变量,因为归属for循环的语法结构中,在for循环结束后,就不能再次被访问到了
- 条件控制语句所控制的自增变量,对于while循环来说不归属其语法结构中,在while循环结束后,该变量还可以继续使用
- 死循环(无限循环)的三种格式
- for(;😉{}
- while(true){}
- do {} while(true);
10. 跳转控制语句
- 跳转控制语句(break)
- 跳出循环,结束循环
- 跳转控制语句(continue)
- 跳过本次循环,继续下次循环
- 注意: continue只能在循环中进行使用!
11 . 习题
打印1-100质数
public class test{
public static void main(String[] args){
//打印1-100质数
//1、外层循环作为被除数,内层循环作为除数。
//
//2、定义一个开关,标记外层循环数是否为质数。默认为 true
//
//3、内层循环结束,如果开关还为true。即被除数为质数,打印出来
for (int i = 2; i < 100; i++) {
boolean zhiShu = true;
for (int j = 2; j < i - 1; j++) {
if (i % j == 0) {
zhiShu = false;
break;
}
}
if (zhiShu) {
System.out.println(i);
}
}
}
}
打印三角形
public class Demo10 {
public static void main(String[] args) {
for (int i = 1; i <= 9; i++) { //行数
for (int j = 9; j >=i; j--) { //i=1第一次遍历 打印前9个空白字符 j-- 依次递减
System.out.print(" ");
}
for (int j = 1; j <=i; j++) { //i=1第一次遍历打印第一个* j++ 依次增加
System.out.print("*");
}
for (int j = 1; j < i; j++) { //i=1第一次遍历 i=1 j<1不成立 不打印 j++
System.out.print("*");
}
System.out.println();
}
}
}
**统计“水仙花数”一共有多少个,并在控制台输出个数 **
public class ForTest05 {
public static void main(String[] args) {
//定义变量count,用于保存“水仙花数”的数量,初始值为0
int count = 0;
//输出所有的水仙花数必然要使用到循环,遍历所有的三位数,三位数从100开始,到999结束
for(int i=100; i<1000; i++) {
//在计算之前获取三位数中每个位上的值
int ge = i%10;
int shi = i/10%10;
int bai = i/10/10%10;
//在判定水仙花数的过程中,满足条件不再输出,更改为修改count的值,使count+1
if(ge*ge*ge + shi*shi*shi + bai*bai*bai == i) {
count++;
}
}
//打印输出最终结果
System.out.println("水仙花共有:" + count + "个");
}
}
我国古代数学家张丘建在《算经》一书中提出的数学问题:鸡翁一值钱五,鸡母一值钱三,鸡雏三值钱一。百钱买百鸡,问鸡翁、鸡母、鸡雏各几何?
public class Test05 {
public static void main(String[] args) {
//第1层循环,用于表示鸡翁的范围,初始化表达式的变量定义为 x=0,判断条件是x<=20
for(int x=0; x<=20; x++) {
//第2层循环,用于表示鸡母的范围,初始化表达式的变量定义为 y=0,判断条件是y<=33
for(int y=0; y<=33; y++) {
//这个时候,用于表示鸡雏的变量 z = 100 – x – y
int z = 100 - x - y;
//判断表达式 z%3==0 和表达式 5*x + 3*y + z/3 = 100 是否同时成立
if(z%3==0 && 5*x+3*y+z/3==100) {
System.out.println(x+","+y+","+z);
}
}
}
}
}
通过键盘录入的方式输入星期数(1-7的整数),显示今天的减肥活动,使用switch和if两种判断语句分别完成。
周一:跑步
周二:游泳
周三:慢走
周四:动感单车
周五:拳击
周六:爬山
周日:好好吃一顿
if版:
public class Test01 {
public static void main(String[] args) {
//键盘录入一个星期数,用一个变量接收
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个星期数:");
int week = sc.nextInt();
//对星期数进行判断,这里用 if 语句实现
if (week < 1 || week > 7) {
System.out.println("你输入的星期数有误");
} else if (week == 1) {
System.out.println("跑步");
} else if (week == 2) {
System.out.println("游泳");
} else if (week == 3) {
System.out.println("慢走");
} else if (week == 4) {
System.out.println("动感单车");
} else if (week == 5) {
System.out.println("拳击");
} else if (week == 6) {
System.out.println("爬山");
} else {
System.out.println("好好吃一顿");
}
}
}
switch版:
public class Test02 {
public static void main(String[] args) {
//键盘录入一个星期数,用一个变量接收
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个星期数:");
int week = sc.nextInt();
//对星期数进行判断,这里用 switch 语句实现
switch (week) {
case 1:
System.out.println("跑步");
break;
case 2:
System.out.println("游泳");
break;
case 3:
System.out.println("慢走");
break;
case 4:
System.out.println("动感单车");
break;
case 5:
System.out.println("拳击");
break;
case 6:
System.out.println("爬山");
break;
case 7:
System.out.println("好好吃一顿");
break;
default:
System.out.println("你输入的星期数有误");
}
}
}
朋友聚会的时候可能会玩一个游戏:逢七过。
规则是:从任意一个数字开始报数,当你要报的数字包含7或者是7的倍数时都要说:过。
为了帮助大家更好的玩这个游戏,这里我们直接在控制台打印出1-100之间的满足逢七必过规则的数据。
这样,大家将来在玩游戏的时候,就知道哪些数据要说:过
public class Test03 {
public static void main(String[] args) {
//数据在1-100之间,用for循环实现数据的获取
for(int x=1; x<=100; x++) {
//根据规则,用if语句实现数据的判断:要么个位是7,要么十位是7,要么能够被7整除
if(x%10==7 || x/10%10==7 || x%7==0) {
//在控制台输出满足规则的数据
System.out.println(x);
}
}
}
}
有一个很有名的数学逻辑题叫做不死神兔问题。有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问第二十个月的兔子对数为多少?
从第三项开始=前两项之和
f(n)=f(n-1)+f(n-2)
public class Test04 {
public static void main(String[] args) {
//为了存储多个月的兔子对数,定义一个数组,用动态初始化完成数组元素的初始化,长度为20
int[] arr = new int[20];
//因为第1个月,第2个月兔子的对数是已知的,都是1,所以数组的第1个元素,第2个元素值也都是1
arr[0] = 1;
arr[1] = 1;
//用循环实现计算每个月的兔子对数
for(int x=2; x<arr.length; x++) {
arr[x] = arr[x-2] + arr[x-1];
}
//输出数组中最后一个元素的值,就是第20个月的兔子对数
System.out.println("第二十个月兔子的对数是:" + arr[19]);
}
}
有这样的一个数组,元素是{68,27,95,88,171,996,51,210}。求出该数组中满足要求的元素和,
要求是:求和的元素个位和十位都不能是7,并且只能是偶数。
public class Test06 {
public static void main(String[] args) {
//定义一个数组,用静态初始化完成数组元素的初始化
int[] arr = {68, 27, 95, 88, 171, 996, 51, 210};
//定义一个求和变量,初始值是0
int sum = 0;
//遍历数组,获取到数组中的每一个元素
for(int x=0; x<arr.length; x++) {
//判断该元素是否满足条件,如果满足条件就累加
if(arr[x]%10!=7 && arr[x]/10%10!=7 && arr[x]%2==0) {
sum += arr[x];
}
}
//输出求和变量的值
System.out.println("sum:" + sum);
}
}
设计一个方法,用于比较两个数组的内容是否相同。
public class Test07 {
public static void main(String[] args) {
//定义两个数组,分别使用静态初始化完成数组元素的初始化
int[] arr = {11, 22, 33, 44, 55};
//int[] arr2 = {11, 22, 33, 44, 55};
int[] arr2 = {11, 22, 33, 44, 5};
//调用方法,用变量接收
boolean flag = compare(arr,arr2);
//输出结果
System.out.println(flag);
}
//定义一个方法,用于比较两个数组的内容是否相同
public static boolean compare(int[] arr, int[] arr2) {
//首先比较数组长度,如果长度不相同,数组内容肯定不相同,返回false
if(arr.length != arr2.length) {
return false;
}
//其次遍历,比较两个数组中的每一个元素,只要有元素不相同,返回false
for(int x=0; x<arr.length; x++) {
if(arr[x] != arr2[x]) {
return false;
}
}
//最后循环遍历结束后,返回true
return true;
}
}
已知一个数组 arr = {19, 28, 37, 46, 50}; 键盘录入一个数据。定义一个方法,完成查找该数据在数组中的索引,并在控制台输出找到的索引值。
public class Test08 {
public static void main(String[] args) {
//定义一个数组,用静态初始化完成数组元素的初始化
int[] arr = {19, 28, 37, 46, 50};
//键盘录入要查找的数据,用一个变量接收
Scanner sc = new Scanner(System.in);
System.out.println("请输入要查找的数据:");
int number = sc.nextInt();
//调用方法
int index = getIndex(arr, number);
//输出索引变量
System.out.println("index: " + index);
}
//查找指定的数据在数组中的索引
public static int getIndex(int[] arr, int number) {
//定义一个索引变量,初始值为-1
int index = -1;
//遍历数组,获取到数组中的每一个元素
for(int x=0; x<arr.length; x++) {
//拿number和数组中的每一个元素进行比较
//如果值相同,就把该值对应的索引赋值给索引变量,并结束循环
if(arr[x] == number) {
index = x;
break;
}
}
//返回索引
return index;
}
}
已知一个数组 arr = {19, 28, 37, 46, 50}; 用程序实现把数组中的元素值反转(在原数组中操作,不能定义第二个数组),反转后的数组 arr = {50, 46, 37, 28, 19}; 并在控制台输出反转后的数组元素。
public class Test09 {
public static void main(String[] args) {
//定义一个数组,用静态初始化完成数组元素的初始化
int[] arr = {19, 28, 37, 46, 50};
//调用反转的方法
reverse(arr);
//遍历数组
printArray(arr);
}
//定义反转方法
public static void reverse(int[] arr) {
//循环遍历数组,这一次初始化语句定义两个索引变量,判断条件是开始索引小于等于结束索引
for (int start = 0, end = arr.length - 1; start <= end; start++, end--) {
//变量交换
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}
}
//定义用于打印数组的方法
public static void printArray(int[] arr) {
System.out.print("[");
for (int x = 0; x < arr.length; x++) {
if (x == arr.length - 1) {
System.out.print(arr[x]);
} else {
System.out.print(arr[x] + ", ");
}
}
System.out.println("]");
}
}
在编程竞赛中,有6个评委为参赛的选手打分,分数为0-100的整数分。
选手的最后得分为:去掉一个最高分和一个最低分后 的4个评委平均值 (不考虑小数部分)。
public class Test10 {
public static void main(String[] args) {
//定义一个数组,用动态初始化完成数组元素的初始化,长度为6
int[] arr = new int[6];
//键盘录入评委分数
Scanner sc = new Scanner(System.in);
//由于是6个评委打分,所以,接收评委分数的操作,用循环改进
for(int x=0; x<arr.length; x++) {
System.out.println("请输入第" + (x + 1) + "个评委的打分:");
arr[x] = sc.nextInt();
}
//调用方法获取数组中的最高分
int max = getMax(arr);
//调用方法获取数组中的最低分
int min = getMin(arr);
//调用方法获取数组中的所有元素的和
int sum = getSum(arr);
//按照计算规则进行计算得到平均分
int avg = (sum - max - min) / (arr.length - 2);
//输出平均分
System.out.println("选手的最终得分是:" + avg);
}
//定义求和方法
public static int getSum(int[] arr) {
int sum = 0;
for(int x=0; x<arr.length; x++) {
sum += arr[x];
}
return sum;
}
//定义求最小值方法
public static int getMin(int[] arr) {
int min = arr[0];
for(int x=1; x<arr.length; x++) {
if(arr[x] < min) {
min = arr[x];
}
}
return min;
}
//定义求最大值方法
public static int getMax(int[] arr) {
int max = arr[0];
for(int x=1; x<arr.length; x++) {
if(arr[x] > max) {
max = arr[x];
}
}
return max;
}
//定义遍历数组方法
public static void printArray(int[] arr) {
System.out.print("[");
for (int x = 0; x < arr.length; x++) {
if (x == arr.length - 1) {
System.out.print(arr[x]);
} else {
System.out.print(arr[x] + ", ");
}
}
System.out.println("]");
}
}
java方法
1. 方法的定义
修饰符 返回值类型 方法名(参数类型 参数名){
...
方法体
...
return 返回值;
}
public int add(int a,int b){
int sum;
sum = a+b;
return sum;
}
- Java方法是一段用来完成特定功能的代码片段,一般情况下一个方法包含以下语法
- 方法包含一个方法头和一个方法体
- 修饰符:可选的,告诉编译器如何调用该方法,定义方法的访问类型。(如:static,final)
- 返回值类型:返回方法的返回值。returnValueType是方法返回值的数据类型。有些方法没有返回值关键词是void
- 方法名:是方法的实际名称。
- 参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型,顺序和参数的个数。参数是可选的,可以不包含任何参数。
- 形式参数:在方法被调用时用于接收外界输入的数据
- 实参:调用方法时实际传给方法的数据
- 方法体:具体的语句,定义方法的功能。
2. 方法调用
- 对象名.方法名(实参列表)
- 值传递:Java中只有值传递 ,值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量.
- 引用传递:引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身
3. 方法重载
-
重载:在一个类中,方法名相同,但参数列表不同
-
规则:
- 方法名必须相同
- 参数列表必须不相同(个数不同,或类型不同,参数排序顺序不同)
- 方法的返回值类型可以相同可以不相同
- 仅仅返回值类型不同不足以成为方法的重载
4. 可变参数
-
在方法声明中,指定参数类型后面加一个省略号(…)
-
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
public static void printMax(double... numbers){ if(numbers.lenth == 0){ System.out.printlt("No"); return; } double result = numbers[0]; for(int i =1;i<numbers.length;i++){ if(numbers[i]>result){ result=number[i]; } } System.out.printl(result); }
5. 递归
递归就是一个程序或函数在其中定义或说明有之间或者间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个原问题相似的规模较小的问题来求解,递归策略只需要少量的程序就可以描述出解题过程所需要的多次重复计算,大大的减少了程序的代码量,递归的能力在于用有限的语句来定义对象的无限集合,一般来说,递归需要边界条件,递归前进段和递归返回段,当边界条件不满足时,递归前进,当边界条件满足时,递归返回。
说白了就是方法自己调用自己。
递归结构包含两部分:
递归头:什么时候不调用自己的方法。如果没有头,将陷入死循环。
递归体:什么时候需要调用自己的方法。
public static void main(String[] args) {
//递归阶乘
System.out.println(f(100));
}
public static int f(int n) {
if (n == 1) {
return 1;
} else {
return n * f(n - 1);
}
}
123456789101112
数组
1. 数组的定义
- 数组是相同类型数据的有序集合
- 数组描述的是相同类型的若干个数据,按照一定的先后次序排序组合而成
- 其中每一个数据被称做一个数组元素,每一个数组元素可以通过一个下标来访问他们
2. 数组声明创建
- 首先必须声明数组变量,才能在程序中使用数组
int[] arrays;
int arrays[];
- Java语言使用new操作来创建数组
int[] arrays = new int[5];
- 数组的元素是通过索引访问的,数组索引从0开始
- 获取数组长度:arrays.length
3. 数组内存分析
-
数组的默认初始化:
数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间(在堆中初始化大小了),其中每个元素也按照实例变量同样的方式被隐式初始化了
4. 数组的基本特点
- 数组的长度是确定的,数组一旦被创建,它的大小就不可以被改变。
- 其元素必须是相同类型,不允许出现混合类型。
- 数组中定义的元素类型可以是任何类型,包括基本类型和引用类型。
- 数组属于引用类型,数组也可以被看作是对象。数组中每个元素相当于对象中的成员变量,数组本身就是对象,Java中对象在堆中,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。
5. 数组边界
- 数组也是对象,数组元素相当于对象的成员变量。
- 数组长度是确定的,不可变的。如果越界,则报:ArrayIndexOutofBoundsException 数组下标越界异常
6. 数组使用
public class Demo01 {
public static void main(String[] args) {
//创建数组
int[] arrays = new int[10];
int arrays1[] = new int[10];
int[] arrays02 = {1,2,3,4,5,6,7,8,9,10};
}
}
public class Demo02 {
public static void main(String[] args) {
int[] array = {1,23,4,56,3};
//打印所有元素
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
//计算所有元素总和
int num = 0;
for (int i = 0; i < array.length; i++) {
num+=array[i];
}
System.out.println(num);
//查找最大元素
int max = 0;
for (int i = 0; i < array.length; i++) {
if (max<array[i]){
max = array[i];
}
}
System.out.println(max);
//增强for循环遍历数组
for (int i : array) {
System.out.println(i);
}
}
}
public class Demo03 {
//数组反转
public static void main(String[] args) {
int[] list={1,2,3,4,5};
System.out.println(Arrays.toString(reverse(list)));
}
public static int[] reverse(int[] list) {
int[] result = new int[list.length];
for (int i = 0, j = result.length - 1; i < list.length; i++, j--) {
result[j] = list[i];
}
return result;
}
}
7. Arrays类
- 数组的工具类Java.util.Arrays
- Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用。
- 给数组赋值:file方法
- 给数组排序:sort方法
- 比较数组:equals方法比较数组元素值是否相等
- 查找数组元素:通过binarySearch方法能对排序号的数组进行二分查找法操作
8. 冒泡排序
- 实现思路:
- 两层循环,外层循环轮数,内循环比较大小。
- 用二重循环实现,外循环变量设为i,内循环变量设为j。假如有n个数需要进行排序,则外循环重复n-1次,内循环依次重复n-1,n-2,…,1次。每次进行比较的两个元素都是与内循环j有关的,它们可以分别用a[j]和a[j+1]标识,i的值依次为1,2,…,n-1,对于每一个i,j的值依次为0,1,2,…n-i
//冒泡排序
//1.比较数组中两个相邻的元素,如果第一个数比第二个数大,交换位置
//2.每一次比较,都会产生出一个最大,或者最小的数字
//3.下一轮减少一次排序
//4.依次循环,直到结束
public class Demo04 {
public static void main(String[] args) {
int[] arr = {3,1,45,21,34,2};
int[] sort = sort(arr);
System.out.println(Arrays.toString(sort));
}
public static int[] sort(int[] array){
int temp = 0;
//外层循环,判断比较次数
for (int i = 0; i < array.length-1; i++) { //比较的次数
//内层循环,比较数值大小
for (int j = 0; j < array.length - 1 - i; j++) { //比较大小
if (array[j+1]<array[j]){ //如果后一位数比前一位数大进行交换 从小到大排序<号,从大到小>号
temp = array[j+1]; // c=a
array[j+1] = array[j]; // a=b
array[j] = temp; // b=c
}
}
}
return array;
}
}
面向对象
1. 什么是面向对象
面向对象(Object Oriented)是一种新兴的程序设计方法,或者是一种新的程序设计规范(paradigm),其基本思想是使用对象、类、继承、封装、多态等基本概念来进行程序设计。从现实世界中客观存在的事物(即对象)出发来构造软件系统,并且在系统构造中尽可能运用人类的自然思维方式。
面向对象的本质:以类的方式组织代码,以对象的方式组织(封装)数据
面向对象的核心思想:抽象
面向对象的的三大特征:封装、多态、继承
在面向对象之前都是面向过程编程方式;
面向过程思想:
- 步骤清晰简单,第一步做什么,第二步做什么。。。。
- 面向过程适合处理一些简单的问题
面向对象思想:
- 物以类聚,分类的思维模式,思考问题首先会解决问题需要那些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的探索。
- 面向对象适合处理复杂的问题,适合处理多人协作的问题
对于描述复杂的事物,为了宏观上的把握,从整体合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍需要面向过程去处理;
2. 类与对象的关系
- 类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物
- 对象是抽象的具体实例,能够体现出特点,展现出功能的具体实例
- 类
- 类的理解
- 类是对现实生活中一类具有共同属性和行为的事物的抽象
- 类是对象的数据类型,类是具有相同属性和行为的一组对象的集合
- 简单理解:类就是对现实事物的一种描述
- 类的组成
- 属性:指事物的特征,例如:手机事物(品牌,价格,尺寸)
- 行为:指事物能执行的操作,例如:手机事物(打电话,发短信)
- 类的理解
- 类和对象的关系
- 类:类是对现实生活中一类具有共同属性和行为的事物的抽象
- 对象:是能够看得到摸的着的真实存在的实体
- 简单理解:类是对事物的一种描述,对象则为具体存在的事物
3. 创建与初始化对象
- 使用new关键字创建对象
- 使用new关键字创建对象的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
成员变量和局部变量区别
- 类中位置不同:成员变量(类中方法外)局部变量(方法内部或方法声明上)
- 内存中位置不同:成员变量(堆内存)局部变量(栈内存)
- 生命周期不同:成员变量(随着对象的存在而存在,随着对象的消失而消失)局部变量(随着方法的调用而存在,随着方法的调用完毕而消失)
- 初始化值不同:成员变量(有默认初始化值)局部变量(没有默认初始化值,必须先定义,赋值才能使用)
4. 构造器
构造函数(构造器、构造函数):构造函数是一种特殊的函数。其主要功能是用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。
A.方法名与类名相同;
B.没有返回类型(例如return、void等);
C.不能被static、final、native、abstract和synchronized修饰,不能被子类继承。
D.父类的构造方法不能被子类调用,可以通过super语句调用父类的构造方法。
E.构造方法可以重载,以参数的个数,类型,顺序,分为空参构造方法和有参构造方法
-
类中的构造器也称构造方法,是在进行创建对象的时候必须要调用的。
-
特点
- 必须和类的名字相同
- 必须没有返回类型,也不能写void
- 构造方法不能被static,final等关键字修饰,且不能有return语句。
-
一个类即使什么都不写也会存在一个方法,无参构造。
-
构造器的作用就是实例化初始值;
-
在使用new关键字的时候,本质就是调用了无参构造器;有参构造,一旦定义了有参构造,无参构造必须显示定义;
public class Student{
String name;
public Student(){}//无参构造
public Student(String name){
this.name = name;
}//有参构造
}
5. 面向对象的三大特征
5.1 封装
通常应禁止直接访问一个对象中数据的实际表示,应通过操作接口来访问
实现高内聚,低耦合
高内聚:就是类的内部数据细节自己操作完成,不允许外部干涉
低耦合:仅暴露少量的方法给外部使用;
- 封装概述
是面向对象三大特征之一(封装,继承,多态)
是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界是无法直接操作的 - 封装原则
将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
成员变量private,提供对应的getXxx()/setXxx()方法 - 封装好处
通过方法来控制成员变量的操作,提高了代码的安全性
把代码用方法进行封装,提高了代码的复用性
封装的优点
- 良好的封装能够减少耦合。
- 类内部的结构可以自由修改。
- 可以对成员变量进行更精确的控制。
- 隐藏信息,实现细节。
**属性私有,get/set **,在set值得时候,可以在类中对应的方法加条件判断,比如一些值范围等…
public class Student{
private int age;
//采用 this 关键字是为了解决实例变量(private int age)和局部变量(setAge(int age)中的age变量)之间发生的同名的冲突。
public void setAge(int age){
if(age > 100 || age < 0){
this.age = age;
}else{
this.age = 60;
}
}
}
5.2 继承
在Java中所有类,都默认直接或间接继承了Object类。
在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:
类的继承格式:class 父类 { } class 子类 extends 父类 { }
java只有单继承,没有多继承。但支持多重继承
继承的特性
- 子类拥有父类非 private 的属性、方法。
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
- Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
- 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
5.3 多态
多态是同一个行为具有多个不同表现形式或形态的能力。
多态的优点
- 消除类型之间的耦合关系
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性
多态存在的三个必要条件
- 继承
- 重写
- 父类引用指向子类对象
父类:
public class Father{
public void run(){
System.out.print("跑");
}
}
子类:
public class Son extent Father{
public void run(){
System.out.print("跑跑");
}
}
main方法:
public static void main(String[] agrs){
//一个对象的实际类型是确定的
//可以指向的引用类型就不确定了,父类的引用类型可以指向子类
Son s1 = new Son(); //可以调用自己的或者继承父类的
Father f1 = new Son(); //可以指向子类,但是不能调用子类的方法。 Father就是引用类型,Son是实际类型
s1.run();
f1.run();
//结果都是跑跑,因为子类继承并实现了父类方法,调用时则使用子类的;
// 对象能执行那些方法主要看左边引用类型的,和右边关系不大!
//多态是方法的多态,属性没有多态!
}
6. 方法重写
重写:需要有继承关系,子类重写父类的方法。子类的方法和父类的必须要一致,方法体不同。
- 方法名必须相同
- 参数列表必须相同
- 返回值类型需相同
- 访问权限:子类重写父类的方法的访问权限可以和父类一样活着更小,但不能扩大。public > protected > default > private
- 抛出异常:子类重写父类的方法不能比父类抛出的异常范围更大,可以更小。ClassNotFoundException(小) --> Exception(大)
- 声明为 final 的方法不能被重写
- 声明为 static 的方法不能被重写,但是能够被再次声明
- 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法
- 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法
- 构造方法不能被重写
重载和重写的区别:
-
重载:在一个类中,方法名相同,但参数列表不同
-
规则:
- 方法名必须相同
- 参数列表必须不相同(个数不同,或类型不同,参数排序顺序不同)
- 方法的返回值类型可以相同可以不相同
- 仅仅返回值类型不同不足以成为方法的重载
为什么需要重写:父类的功能子类不一定需要不一定满足。
构造方法为什么不能重写
重写是子类方法重写父类的方法,重写的方法名不变,而类的构造方法名必须与类名一致,假设父类的构造方法如果能够被子类重写则子类类名必须与父类类名一致才行。
构造器是不能被继承的,因为每个类的类名都不相同,而构造器名称与类名相同,所以根本谈不上继承。
又由于构造器不能继承,所以就不能被重写。但是,在同一个类中,构造器是可以被重载的。
7. super - this
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。(调用父类,继承后在可以用)(父类的构造)
this关键字:指向自己的引用。(调用自己类,没有继承也可以使用)(本类的构造)
this修饰的变量用于指代成员变量,其主要作用是(区分局部变量和成员变量的重名问题)
- 方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量
- 方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量
super注意:
- super调用父类的构造方法必须在子类的构造方法代码的第一行
- super只能出现在子类的方法或者构造方法中
- super和this不能同时调用构造方法
代表对象的不同:
- this:本身调用者这个对象
- super:代表父类对象的应用
前提:
- this:没有继承也可以使用
- super:只能在继承条件下才能使用
构造方法:
- this:本类的构造
- super:父类的构造
8. static
**static关键字:**方便在没有创建对象的情况下来进行调用(方法/变量)
-
被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。
-
static可以用来修饰类的成员方法、类的成员变量,另外可以编写static代码块来优化程序性能
-
static修饰的方法为静态方法:由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。
-
在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。但是在非静态成员方法中是可以访问静态成员方法/变量的
-
static修饰的变量是静态变量:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
-
static修饰的代码块是静态代码块:在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
静态方法可不易直接调用非静态方法,非静态方法可以直接调用静态方法,因为静态方法在程序运行的时候已经被初始化!
调用静态方法 类名 . 静态方法名; 非静态的需要将类初始化后在点。
还可以静态导入包:
import static java.lang.Math.random; //不加static是错误的;
public class Test{
{
System.out.print("非静态代码块");
}
static{
System.out.print("静态代码块");
}
public Test(){
System.out.print("构造方法");
}
//加载顺序 静态代码块---非静态代码块---构造方法
}
instanceof关键字:判断一个对象是什么类型,判断两个类是否存在父子关系
9. 抽象类 abstract
抽象方法的 3 个特征:
- 抽象方法没有方法体
- 抽象方法必须存在于抽象类中
- 子类重写父类时,必须重写父类所有的抽象方法
注意:在使用 abstract 关键字修饰抽象方法时不能使用 private 修饰,因为抽象方法必须被子类重写,而如果使用了 private 声明,则子类是无法重写的。
- 抽象类使用abstract来修饰
- 如果一个方法使用abstract修饰那么这个类必须是抽象类,使用abstract修饰
- 抽象类中可以没有抽象方法,但有抽象方法的类一定要声明为抽象类
- 抽象类不能被实例化,不能使用new关键字创建
- 抽象类可以含有抽象方法,也可以不包含抽象方法,抽象类中可以有具体的方法
- 子类继承抽象类,那么必须实现抽象类中没有实现的抽象方法,否则该子类也要声明为抽象类。
//类中有抽象方法那么该类必须使用abstract修饰为抽象类
public abstract class A {
//抽象类中可以定义任意类型变量
int a = 10;
int b;
static int c;
static final int d = 1;
public abstract void add();//抽象方法不能有方法体
}
10. 接口 interface
普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有
接口:只是规范
-
接口使用interface修饰
-
方法默认是使用public abstract修饰的,抽象方法
-
接口中声明的变量其实都是常量,定义的常量是使用public static final修饰的静态常量,所以接口中定义的变量必须初始化。
-
接口不能被实例化,接口中没有构造方法
-
implements可以实现多个接口
-
一个类实现一个接口必须重写接口中的方法
public interface B {
//接口中只能定义常量默认使用 public static final 修饰
public static final int a = 1;
//接口中方法默认使用 public abstract 修饰
public abstract void add();
//接口中不能有静态代码块,静态方法
//static {
//}
//public abstract static void delete();
}
接口为什么没有构造方法
- 类可以实现多个接口,若多个接口都有自己的构造器,则不好决定构造器链的调用次序
- 构造函数用于初始化非静态数据成员,由于接口中没有非静态数据成员,因此不需要构造函数
- 接口中存在的方法仅声明为未定义。由于没有方法的实现,因此无需在接口中创建用于调用方法的对象,因此无需在其中构造函数
- 如果我们尝试在接口内创建构造函数,则编译器将给出编译时错误。
- 在接口里写入构造方法时,编译器提示:Interfaces cannot have constructors
抽象类中为什么有构造方法
- 在抽象类中可以有构造方法,只是不能直接创建抽象类的实例对象,但实例化子类的时候,就会初始化父类,不管父类是不是抽象类都会调用父类的构造方法,初始化一个类,先初始化父类。
构造方法
-
构造方法(构造器、构造函数):构造函数是一种特殊的函数。其主要功能是用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。
-
方法名与类名相同;
-
没有返回类型(例如return、void等);
-
不能被static、final、native、abstract和synchronized修饰,不能被子类继承。
-
父类的构造方法不能被子类调用,可以通过super语句调用父类的构造方法。
-
构造方法可以重载,以参数的个数,类型,顺序,分为空参构造方法和有参构造方法
11. 抽象类和接口的区别
-
抽象类可以提供成员方法的实现细节,而接口中没有;但是JDK1.8之后,在接口里面可以定义default方法,default方法里面是可以具备方法体的。
-
抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是
public static final
类型; -
接口中每一个方法也是隐式指定为 public abstract,不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
(接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错),所以不能含有静态代码块以及静态方法(用 static 修饰的方法))
-
一个类只能继承一个抽象类,而一个类却可以实现多个接口。
12. 内部类
- 内部类就是在一个类的内部再定义一个类。A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类
- 内部类可以分为:局部内部类、静态内部类、成员内部类、匿名内部类
- 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的
.class
文件,但是前面冠以外部类的类名和$
符号。 - 内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否为 private 的。
- 内部类声明成静态的,就不能随便访问外部类的成员变量,仍然是只能访问外部类的静态成员变量。
public class Demo04 {
int a = 10;
public void add(){
//匿名内部类 没有对象名
new Dog().eat();
new UserService() {
@Override
public void add() {
}
};
//局部内部类
class inner{
public void add(){
System.out.println(a);
}
}
System.out.println(a+"1");
}
//成员内部类
class Method{
public void add(){
System.out.println(a);
}
}
//静态内部类 只能访问静态变量
static class Method02{
public void add(){
//System.out.println(a);
}
}
interface UserService{
public void add();
}
}
java异常机制
1. 什么是异常
异常通常指,你的代码可能在编译时没有错误,可是运行时会出现异常。比如常见的空指针异常。也可能是程序可能出现无法预料的异常,比如你要从一个文件读信息,可这个文件不存在,程序无法运行下去了,故程序要抓这些异常,通过异常处理机制来抛出这些异常,程序员就可以通过抛出的异常来修改代码
2. 异常分类
- 检查性异常:例如要打开一个不存在的文件,异常就发生了,这些异常再编译时不能被简单忽略
- 运行时异常:是可以被人为避免的异常,与检查时异常相反,运行时异常可以在编译时被忽略
- 错误:错误不是异常,而是脱离人为控制的问题。错误在代码中通常被忽略。例如,当栈内存溢出时,他在编译时也检查不到。
3. 异常体系结构
- Java把异常当作对象处理,定义一个基类java.lang.Throwable作为异常的超类。
- 异常类被分为两大类,错误Error和Exception
Throwable中的常用方法:
-
public void printStackTrace()
:打印异常的详细信息。包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。
-
public String getMessage()
:获取发生异常的原因。提示给用户的时候,就提示错误原因。
-
public String toString()
:获取异常的类型和异常描述信息(不用)。 -
Thorwable类(表示可抛出)是所有异常和错误的超类,两个直接子类为Error和Exception,分别表示错误和异常。其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常, 这两种异常有很大的区别,也称之为不检查异常(Unchecked Exception)和检查异常(Checked Exception)。
-
Error与Exception
- Error是程序无法处理的错误,它是由JVM产生和抛出的,比如OutOfMemoryError、ThreadDeath等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。程序中应当尽可能去处理这些异常。
- Error是程序无法处理的错误,它是由JVM产生和抛出的,比如OutOfMemoryError、ThreadDeath等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
-
运行时异常和非运行时异常
- 运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
- 运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
-
算术异常类:ArithmeticExecption
空指针异常类:NullPointerException
类型强制转换异常:ClassCastException
数组负下标异常:NegativeArrayException
数组下标越界异常:ArrayIndexOutOfBoundsException
违背安全原则异常:SecturityException
文件已结束异常:EOFException
文件未找到异常:FileNotFoundException
字符串转换为数字异常:NumberFormatException
操作数据库异常:SQLException
输入输出异常:IOException
方法未找到异常:NoSuchMethodException
4. 异常处理机制
try catch finally throw throws
抛出异常
如果一个方法没有捕获到一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。
也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
throw常用在方法体内,throws 则用在方法上抛出异常;
public void deposit(double amount) {
// Method implementation
throw new RemoteException();
}
//一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。
public void withdraw(double amount) throws RemoteException,
InsufficientFundsException
{
// Method implementation
}
//Remainder of class definition
捕获异常
使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。
try/catch代码块中的代码称为保护代码,使用 try/catch 的语法如下:
try
{
// 程序代码
}catch(ExceptionName e1)
{
//在Catch时尽可能在最下面放上最大的异常Exception 以免捕获不到;
//在处理异常的时候推荐积极的打印异常做好判断然后打印;
//Catch 块 Catch 可以有多个
}catch(ExceptionName e1)
{
//Catch 块 Catch 可以有多个
}finally{
// 最后要执行的代码 可有可无
}
Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。
如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。
finally 关键字用来创建在 try 代码块后面执行的代码块。无论是否发生异常,finally 代码块中的代码总会被执行。在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。比如IO流时发生异常最后在finally内关闭IO流;
注意事项:
- catch 不能独立于 try 存在。
- 在 try/catch 后面添加 finally 块并非强制性要求的。
- try 代码后不能既没 catch 块也没 finally 块。
- try, catch, finally 块之间不能添加任何代码。
5. 自定义异常
在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
- 所有异常都必须是 Throwable 的子类。
- 如果希望写一个检查性异常类,则需要继承 Exception 类。
- 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
//自定义异常类
public class MyException extends Exception{
private int detail;//传递的值
public MyExcrption(int a){
this.detail = a;
}
//打印异常信息
public String toString(){
return "MyException{"+detail+"}";
}
}
6. 总结
- 处理运行时异常,采用逻辑去合理规避同时辅助try-catch处理
- 在多重catch块后面,可以加一个catch(Exception)来处理可能会遗漏的异常
- 对于不确定的代码,也可以加和那个一个try-catch,处理潜在异常
- 尽量去处理异常,切忌只是简单的调用printStackTrace()去打印
- 具体如何处理异常,根据不同的业务需求和异常类型决定
- 尽量添加finally语句块去释放占用资源
final finally finalize区别
final :
- final修饰的类是不能被继承的,因为其是一个最终类;
- final修饰的变量是一个常量,只能被赋值一次;
- final修饰的方法也不能重写,但能被重载;
- final可以修饰类、方法、变量;
- 内部类只能访问被final修饰的局部变量。
finally :
- finally块通常放在try、catch的后面,有时可以直接放在try 的后面,但有时会不能放。
- finally中的语句是正常执行或者处理异常之后必须执行的语句,finally块一般是用来关闭(释放)物理资源(数据库连接,网络连接,磁盘文件等)。无论是否发生异常,资源都必须进行关闭。
- 当没有必要资源需要释放时,可以不用定义finally块。
- finally块中的代码总能执行,这就说明无论try、catch块中执行怎样的代码,是否发生异常,还是正常运行,finally块一定会被执行,如果想要finally块不执行,除非在try、catch块中调用退出虚拟机的方法,否则finally块无论怎么样还是会被执行
finalize
- Java中的一个方法名。
- Java技术使用finalize()方法在垃圾收集器将对象从内存中清除出去前,做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没被引用时对这个对象调用的。它是在Object类中定义的,因此所的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。
String StringBuilder StringBuffer
1. String
- 字符串不可变,它们的值在创建后不能被更改
- 虽然 String 的值是不可变的,但是它们可以被共享
- 字符串效果上相当于字符数组( char[] ),但是底层原理是字节数组( byte[] )
常用的构造方法
方法名 | 说明 |
---|---|
public String() | 创建一个空白字符串对象,不含有任何内容 |
public String(char[] chs) | 根据字符数组的内容,来创建字符串对象 |
public String(byte[] bys) | 根据字节数组的内容,来创建字符串对象 |
String s = “abc”; | 直接赋值的方式创建字符串对象,内容就是abc |
public class Demo05 {
public static void main(String[] args) {
//直接赋值的方式创建字符串对象,内容就是abc
String a = "abc";
String s = new String(a);
System.out.println("s="+s);
//根据字符数组的内容,来创建字符串对象
char[] chars = {'a','b','c'};
String s1 = new String(chars);
System.out.println("s1="+s1);
//根据字节数组的内容,来创建字符串对象
byte[] bytes = {'a','b','c'};
String s2 = new String(bytes);
System.out.println("s2="+s2);
//创建一个空白字符串对象,不含有任何内容
String s3 = new String();
System.out.println("s3="+s3);
}
}
1.1 创建字符串对象两种方式的区别
-
通过构造方法创建
通过 new 创建的字符串对象,每一次 new 都会申请一个内存空间,虽然内容相同,但是地址值不同
-
直接赋值方式创建
以“”方式给出的字符串,只要字符序列相同(顺序和大小写),无论在程序代码中出现几次,JVM 都只会建立一个 String 对象,并在字符串池中维护
1.2 ==和equals区别
==号的作用
- 比较基本数据类型:比较的是具体的值
- 比较引用数据类型:比较的是对象的地址值
equals作用
- 重写equals比较内容值,不重写比较地址值
- Objects类优化空指针异常
public class de {
public static void main(String[] args) {
Student student = new Student("1", "zz", "21", "qq");
Student student2 = new Student("1", "zz", "21", "qq");
System.out.println(student==student2);//比较对象地址值
System.out.println(student.equals(student2));//重写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 id.equals(student.id) &&
name.equals(student.name) &&
age.equals(student.age) &&
address.equals(student.address);
}
@Override
public int hashCode() {
return Objects.hash(id, name, age, address);
}
}
2. 习题
1.已知用户名和密码,请用程序实现模拟用户登录。总共给三次机会,登录之后,给出相应的提示
/*
思路:
1:已知用户名和密码,定义两个字符串表示即可
2:键盘录入要登录的用户名和密码,用 Scanner 实现
3:拿键盘录入的用户名、密码和已知的用户名、密码进行比较,给出相应的提示。字符串的内容比较,用equals() 方法实现
4:用循环实现多次机会,这里的次数明确,采用for循环实现,并在登录成功的时候,使用break结束循环
*/
public class Demo06 {
public static void main(String[] args) {
//已知用户名和密码,请用程序实现模拟用户登录。总共给三次机会,登录之后,给出相应的提示
String userName = "ABC";
String passWord = "123";
for (int i = 0; i <= 3; i++) {
Scanner scanner = new Scanner(System.in);
System.out.println("输入用户名");
String user = scanner.next();
System.out.println("输入密码");
String word = scanner.next();
if (user.equals(userName) && word.equals(passWord)){
System.out.println("登入成功");
break;
}else {
if (2-i==0){
System.out.println("账户已被锁定");
break;
}
System.out.println("你还有"+(2-i)+"次机会");
}
}
}
}
2.键盘录入一个字符串,使用程序实现在控制台遍历该字符串
/*
思路:
1:键盘录入一个字符串,用 Scanner 实现
2:遍历字符串,首先要能够获取到字符串中的每一个字符
public char charAt(int index):返回指定索引处的char值,字符串的索引也是从0开始的
3:遍历字符串,其次要能够获取到字符串的长度
public int length():返回此字符串的长度
数组的长度:数组名.length
字符串的长度:字符串对象.length()
4:遍历字符串的通用格式
*/
public class StringTest02 {
public static void main(String[] args) {
//键盘录入一个字符串,用 Scanner 实现
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String line = sc.nextLine();
for(int i=0; i<line.length(); i++) {
System.out.println(line.charAt(i));
}
}
}
3.键盘录入一个字符串,统计该字符串中大写字母字符,小写字母字符,数字字符出现的次数(不考虑其他字符)
/*
思路:
1:键盘录入一个字符串,用 Scanner 实现
2:要统计三种类型的字符个数,需定义三个统计变量,初始值都为0
3:遍历字符串,得到每一个字符
4:判断该字符属于哪种类型,然后对应类型的统计变量+1
假如ch是一个字符,我要判断它属于大写字母,小写字母,还是数字,直接判断该字符是否在对应的范围即可
大写字母:ch>='A' && ch<='Z'
小写字母: ch>='a' && ch<='z'
数字: ch>='0' && ch<='9'
5:输出三种类型的字符个数
*/
public class Demo08 {
public static void main(String[] args) {
//键盘录入一个字符串,统计该字符串中大写字母字符,小写字母字符,数字字符出现的次数(不考虑其他字符)
Scanner scanner = new Scanner(System.in);
System.out.println("输入字符串");
String string = scanner.nextLine();
int big = 0;
int small = 0;
int num = 0;
for (int i = 0; i < string.length(); i++) {
char c = string.charAt(i);
System.out.println(c);
if (c >='A'&& c <='Z'){
big++;
}else if (c >='a'&& c <='z'){
small++;
}else if (c >='0' && c <='9'){
num++;
}
}
System.out.println(big);
System.out.println(small);
System.out.println(num);
}
}
4.定义一个方法,把 int 数组中的数据按照指定的格式拼接成一个字符串返回,调用该方法,并在控制台输出结果。例如,数组为 int[] arr = {1,2,3}; ,执行方法后的输出结果为:[1, 2, 3]
/*
思路:
1:定义一个 int 类型的数组,用静态初始化完成数组元素的初始化
2:定义一个方法,用于把 int 数组中的数据按照指定格式拼接成一个字符串返回。
返回值类型 String,参数列表 int[] arr
3:在方法中遍历数组,按照要求进行拼接
4:调用方法,用一个变量接收结果
5:输出结果
*/
public class Demo09 {
public static void main(String[] args) {
//定义一个方法,把 int 数组中的数据按照指定的格式拼接成一个字符串返回,调用该方法,
//并在控制台输出结果。例如,数组为 int[] arr = {1,2,3}; ,执行方法后的输出结果为:[1, 2, 3]
int[] arrays = {1,2,3};
String test = test(arrays);
System.out.println(test);
}
public static String test(int[] arr){
String s = new String();
s=s+"[";
for (int i = 0; i < arr.length; i++) {
if (i == arr.length - 1){
s+=arr[i];
}else {
s+=arr[i];
s+=",";
}
}
s+="]";
return s;
}
}
5.定义一个方法,实现字符串反转。键盘录入一个字符串,调用该方法后,在控制台输出结果。例如,键盘录入 abc,输出结果 cba
/*
思路:
1:键盘录入一个字符串,用 Scanner 实现
2:定义一个方法,实现字符串反转。返回值类型 String,参数 String s
3:在方法中把字符串倒着遍历,然后把每一个得到的字符拼接成一个字符串并返回
4:调用方法,用一个变量接收结果
5:输出结果
*/
public class Demo10 {
public static void main(String[] args) {
//定义一个方法,实现字符串反转。键盘录入一个字符串,调用该方法后,在控制台输出结果
//例如,键盘录入 abc,输出结果 cba
Scanner scanner = new Scanner(System.in);
System.out.println("输入字符串");
String s = scanner.nextLine();
String test = test(s);
System.out.println(test);
}
public static String test(String string){
String s = "";
for (int i = string.length()-1; i >=0; i--) {
s+=string.charAt(i);
}
return s;
}
}
3. StringBuilder
StringBuilder 是一个可变的字符串类,我们可以把它看成是一个容器,这里的可变指的是 StringBuilder 对象中的内容是可变的
StringBuilder不能安全使用多线程。 如果需要同步, 那么建议使用StringBuffer
4. StringBuilder类和String类的区别
- String类:内容是不可变的
- StringBuilder类:内容是可变的
5. StringBuilder类的构造方法
- 常用的构造方法
Constructor and Description |
---|
StringBuilder() 构造一个没有字符的字符串构建器,初始容量为16个字符。 |
StringBuilder(CharSequence seq) 构造一个包含与指定的相同字符的字符串构建器 CharSequence 。 |
StringBuilder(int capacity) 构造一个没有字符的字符串构建器,由 capacity 参数指定的初始容量。 |
StringBuilder(String str) 构造一个初始化为指定字符串内容的字符串构建器。 |
6. StringBuilder类添加和反转方法
-
添加和反转方法
方法名 说明 public StringBuilder append(任意类型) 添加数据,并返回对象本身 public StringBuilder reverse() 返回相反的字符序列 public int length() 返回长度,实际存储值 public String toString() 通过toString()就可以实现把StringBuilder转换为String
7. StringBuilder和String相互转换
-
StringBuilder转换为String
public String toString():通过 toString() 就可以实现把 StringBuilder 转换为 String
-
String转换为StringBuilder
public StringBuilder(String s):通过构造方法就可以实现把 String 转换为 StringBuilder
public class Demo12 {
public static void main(String[] args) {
//String 转 StringBuilder
String string = "abc";
StringBuilder stringBuilder = new StringBuilder(string);
stringBuilder.reverse();
System.out.println(stringBuilder);
//StringBuilder 转 String
StringBuilder sb = new StringBuilder("abc");
String s = sb.toString();
System.out.println(s);
}
}
8. String StringBuilder StringBuffer 区别
-
String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,这样不仅效率低下,而且大量浪费有限的内存空间
-
StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象
-
StringBuilder 的方法不是线程安全的(不能同步访问)
-
StringBuilder 相较于 StringBuffer 有速度优势
-
在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类
-
String:字符长度是不可变的,固定的,每新创一个字符就会在堆内存开辟一个新空间,在循环中浪费内存。可以赋空(null)值。
-
StringBuilder:字符长度可变,效率高,线程不安全,不可以赋空(null)值
-
StringBuffer:字符长度可变,效率低,线程安全,不可以赋空(null)值
Arrays数组工具类
1. 概述
构造方法私有,不能创建对象
java.util.Arrays
此类包含用来操作数组的各种方法,比如排序和搜索等。其所有方法均为静态方法
如果指定的数组引用为空,则该类中的方法都抛出一个NullPointerException
2. 方法
public static String toString(int[] a)
:返回指定数组内容的字符串表示形式
public static void sort(int[] a)
:对指定的 int 型数组按数字升序进行排序。
public class Demo14 {
public static void main(String[] args) {
int[] arr = {1,21,2,3,4,5,50,24};
System.out.println(arr);//打印地址值
System.out.println(Arrays.toString(arr));
Arrays.sort(arr);//排序
System.out.println(Arrays.toString(arr));
}
}
Math类
1. 概述
java.lang.Math
类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。类似这样的工具类,其所有方法均为静态方法,并且不会创建对象
//数学类基本运算,求取各种值:绝对值,左右大小值,四舍五入值,如下↓所示:
//public static double abs(double a) :返回 double 值的绝对值:
double d1 = Math.abs(-5.0); //d1的值为5.0
double d2 = Math.abs(5.0); //d2的值为5.0
System.out.println(d1);
System.out.println(d2);
int d3 = Math.abs(-5); //d3的值为5
int d4 = Math.abs(5); //d4的值为5
System.out.println(d3);
System.out.println(d4);
//public static double ceil(double a) :返回大于等于参数的最小的整数,最后把结果变成小数,巧记取右大值
double d5 = Math.ceil(3.3); //d5的值为 4.0,天花板,向上取整,两个整数小,大,取大的那个整数
double d6 = Math.ceil(-3.3); //d6的值为 -3.0,不知道定义什么变量接收,.var自动生成方法的返回值
System.out.println(d5);
System.out.println(d6);
//public static double floor(double a) :返回小于等于参数最大的整数,最后把结果变成小数,巧记取左小值
double d7 = Math.floor(3.3); //d7的值为3.0//地板
double d8 = Math.floor(-3.3); //d8的值为-4.0
System.out.println(d7);
System.out.println(d8);
//public static long round(double a) :四舍五入方法,巧记圆滑的人都会四舍五入
long d9 = Math.round(5.5); //d9的值为6
long d10 = Math.round(5.4); //d10的值为5
System.out.println(d9);
System.out.println(d10);
//扩展
System.out.println(Math.sqrt(4.0));//2.0,求开平方根
System.out.println(Math.pow(2.0, 3.0));//2的3次方法,8.0
public class Test14 {
public static void main(String[] args) {
//请使用Math 相关的API,计算在 -10.8 到5.9 之间,绝对值大于6 或者小于2.1 的整数有多少个?
// 定义变量计数
int count = 0;
// 范围内循环
for (double i = Math.ceil(-10.8); i < 5.9; i++) {//Math.ceil(min),向上取整得到小数-10.0,不断加1减少精度丢失
// 获取绝对值并判断
if (Math.abs(i) > 6 || Math.abs(i) < 2.1) {
// 计数
count++;
//System.out.println((int)i);//把带有0的小数强制为整数输出
}
}
System.out.println("个数为: " + count + " 个");
}
}
Object类
1. 概述
对象,也是一类事物,所以写对象类来模拟,对象在堆内存中创建看不见也摸不着,所以提供toString方法把对象变成带有地址值的字符串. 并提供equals方法比较对象的地址值是否相等,这两个方法子类可以继承,也可以重写做自己想要做的事情,所有的类都具有上面的方法,Object类是所有类的父类,所有类都直接或者间接的继承Object类
2. 方法
public String toString()
:(对象在内存中看不见摸不着,所以提供方法,)把对象变成带有地址值的字符串public boolean equals(Object obj)
:比较两个对象的地址值是否相等,相等返回true,否则返回false
3. toString方法
方法摘要
public String toString()
:返回该对象的字符串表示。
toString方法返回该对象的字符串表示,其实该字符串内容就是对象的类型+@+内存地址值。
由于toString方法返回的结果是内存地址,而在开发中,经常需要按照对象的属性得到相应的字符串表现形式,因此也需要重写它。
覆盖重写
如果不希望使用toString方法的默认行为,则可以对它进行覆盖重写。例如自定义的Person类:
public class Person {
private String name;
private int age;
//能够重写Object类的toString方法,在子类里面按alt insert,即可在idea里面自动生成
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
}
//省略构造器与Getter Setter
}
小贴士: 在我们直接使用输出语句输出对象名的时候,其实通过该对象调用了其toString()方法。
4. equals方法
方法摘要
public boolean equals(Object obj)
:指示其他某个对象是否与此对象“相等”。
调用成员方法equals并指定参数为另一个对象,则可以判断这两个对象是否是相同的。这里的“相同”有默认和自定义两种方式。
默认地址比较
如果没有覆盖重写equals方法,那么Object类中默认进行==
运算符的对象地址比较,只要不是同一个对象,结果必然为false。
对象内容比较
如果希望进行对象的内容比较,即所有或指定的部分成员变量相同就判定两个对象相同,则可以覆盖重写equals方法。例如:
import java.util.Objects;
public class Person {
private String name;
private int age;
//能够重写Object类的equals方法,在子类里面按alt insert,即可在idea里面自动生成
@Override
public boolean equals(Object o) {//如果姓名和年龄相同,同一个对象返回true,否则返回false
// 如果对象地址一样,则认为相同
if (this == o)
return true;
// 如果参数为空,或者类型信息不一样,则认为不同
if (o == null || getClass() != o.getClass())
return false;
// 转换为当前类型
Person person = (Person) o;
// 要求基本类型相等,并且将引用类型交给java.util.Objects类的equals静态方法取用结果
return age == person.age && Objects.equals(name, person.name);
}
}
5. Objects类
在刚才IDEA自动重写equals代码中,使用到了java.util.Objects
类,那么这个类是什么呢?
在JDK7添加了一个Objects工具类,它提供了一些方法来操作对象,它由一些静态方法组成,这些方法是null-save(空指针安全的)或null-tolerant(容忍空指针的),用于计算对象的hashcode、返回对象的字符串表示形式、比较两个对象等.
在比较两个对象的时候,Object的equals方法容易抛出空指针异常,而Objects类中的equals方法就优化了这个问题。方法如下:
public static boolean equals(Object a, Object b)
:判断两个对象是否相等。
我们可以查看一下源码,学习一下:
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));//a不等于null,才调用方法,防止空指针异常
}//null表示没有地址值,没有地址值就找不到对象,更加无法使用对象里面的方法,用null去调用非静态方法运行报错
扩展的源码分析面试题如下:↓
//面试题1:为啥打印s的时候会有s.toString()?扩展,了解println搞事情了:把一个原来整数变成了字符串显示
Student2 s = new Student2("fbb",18);
System.out.println(s);//s.toString(),新的需求:打印学生(对象)看到里面的内容值,而不是地址值
System.out.println(s.toString());//
int[] arr = {1,2,3};
System.out.println(arr);//"[I@6e8dacdf",[I@6e8dacdf
System.out.println(arr.toString());//"[I@6e8dacdf",[I@6e8dacdf
//面试题2:单独打印字符数组名的时候不是地址值而是内容值,是因为println方法重载造成的影响
char[] arr2 = {'a','b','c'};
System.out.println(arr2);//abc
System.out.println(arr2.toString());//[C@1e643faf
Date 时间类
1. 概述
日期类,模拟日期形式的时间,可以调用getTime方法精确到毫秒,得到日期时间的时间毫秒值,其构造方法,无参数表示当前时间,有参数就有参照物时间加上方法传入时间,参照物时间为1970年1月1日0时0分0秒,中国要加多8个小时!
java.util.Date
类 表示特定的瞬间,可以精确到毫秒,表示日期这种形式的时间.
public Date()
:分配Date对象并初始化此对象,以表示分配它的当前时间(可以精确到毫秒)。public Date(long date)
:分配Date对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即1970年1月1日00:00:00 GMT)以来的时间,可以精确到毫秒。
使用无参构造,可以自动设置当前系统时间的毫秒时刻;指定long类型的构造参数,可以自定义毫秒时刻。
构造方法,无参数表示当前时间,有参数就有参照物时间,1970年1月1日0时0分0秒
在使用println方法打印引用数据类型的变量名或者说对象名时,系统默认不写也是写,会调用toString方法!
调用Date类中的toString方法,由于Date类对Object类中的toString方法进行了覆盖重写,所以结果为指定格式带有时期时间的字符串,而不是继承自Object类的的对象的地址值字符串!
方法:
public long getTime()
把日期对象转换成对应的时间毫秒值,可以精确到毫秒,用这个得到时间方法
DateFormat 日期格式化类
日期格式化,把日期格式化为指定的字符串,由于它是抽象类,所以用的是它的子类SimpleDateFormat,通过构造方法传入格式,拿到对象名调用format方法把Date对象格式化为字符串,反过来就是解析,调用的是parse方法
1. 概述
java.text.DateFormat
是日期/时间格式化子类的抽象类,我们通过这个类可以帮我们完成日期和文本之间的转换,也就是可以在Date对象与String对象之间进行来回转换。
- 格式化:按照指定的格式,把Date对象转换为String对象。
- 解析:反过来就是解析,即按照指定的格式,把String对象转换为Date对象。
1.1 构造方法
由于DateFormat为抽象类,不能直接使用,所以需要常用的是它的子类java.text.SimpleDateFormat
。
这个类需要一个模式(格式)来指定格式化或解析的东西要参照的标准。构造方法为:
public SimpleDateFormat(String pattern)
:用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat//把日期格式化指定的字符串比如想得到1988年04月10日,格式为"yyyy年MM月dd日"
1.2 格式规则
常用的格式规则为:
标识字母(区分大小写) | 含义 |
---|---|
y | 年,y是year的缩写 |
M | 月,M是Month的缩写 |
d | 日,d是day的缩写 |
H | 时,H是Hour的缩写 |
m | 分,m是minute的缩写 |
s | 秒,s是second的缩写 |
备注:更详细的格式规则,可以参考SimpleDateFormat类的API文档。
创建SimpleDateFormat对象的代码如:
import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class Demo02SimpleDateFormat {
public static void main(String[] args) {
// 对应的日期格式如:2018-01-16 15:06:38
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
}
1.3 常用方法
DateFormat类的常用方法有:
public String format(Date date)
:格式化,就是把Date对象格式化为字符串,反过来就是解析public Date parse(String source)
:反过来就是解析,把字符串解析为Date对象
format方法
使用format方法的代码为:
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
把Date对象转换成String
*/
public class Demo03DateFormatMethod {
public static void main(String[] args) {
Date date = new Date();
// 创建日期格式化对象,在获取格式化对象时可以指定风格
//DateFormat df = new SimpleDateFormat("yyyy年MM月dd日");//多态的写法
SimpleDateFormat df = new SimpleDateFormat("yyyy年MM月dd日");//子类对象的写法
String str = df.format(date);//时间变成指定格式的字符串
System.out.println(str); // 2008年1月23日
}
}
parse方法
使用parse方法的代码为:
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
把String转换成Date对象
*/
public class Demo04DateFormatMethod {
public static void main(String[] args) throws ParseException {
DateFormat df = new SimpleDateFormat("yyyy年MM月dd日");
String str = "2018年12月11日";
Date date = df.parse(str);
System.out.println(date); // Tue Dec 11 00:00:00 CST 2018
}
}
public class Demo02 {
public static void main(String[] args) throws ParseException {
//计算一个人出生了多少天
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String str = "1997-9-13";
Date date = simpleDateFormat.parse(str);
long time = date.getTime();
long time1 = new Date().getTime();
long time3 = time1 - time;//毫秒
System.out.println(time3/1000);//秒
System.out.println(time3/1000/60);//分钟
System.out.println(time3/1000/60/60);//小时
System.out.println(time3/1000/60/60/24);//天
System.out.println(time3/1000/60/60/24/365);//年
}
}
Calendar日历类
1. 概述
java.util.Calendar
是日历类,在Date后出现,替换掉了许多Date的方法。该类将所有可能用到的时间信息封装为静态成员变量,方便获取。日历类就是方便获取各个时间属性,年,月,日等。
创建子类对象,通过日历类的静态方法getInstance,即得到对象方法获取
Calendar也是抽象类,所以Calendar类在创建对象时不能直接创建,而是其通过静态方法得到子类对象
public static Calendar getInstance(){S s = new S();return s;}
:得到实例,即创建子类对象,使用默认时区和语言环境获得一个日历
例如:
import java.util.Calendar;
public class Demo06CalendarInit {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();//cal日历对象,里面包含年月日等.var生成左边变量接收结果
}
}
1.1 常用方法
根据Calendar类的API文档,常用方法有:
public int get(int field)
:返回给定日历字段的值。字段,指的是年,月,日,等public void set(int field, int value)
:将给定的日历字段设置为给定值。public abstract void add(int field, int amount)
:根据日历的规则,为给定的日历字段添加或减去指定的时间量。public Date getTime()
:返回一个表示此Calendar时间值(从历元到现在的毫秒偏移量)的Date对象。
Calendar类中提供很多成员常量,代表给定的日历字段:
字段值 | 含义 |
---|---|
YEAR | 年 |
DAY_OF_MONTH | 月中的天(几号) |
HOUR | 时(12小时制) |
HOUR_OF_DAY | 时(24小时制) |
MINUTE | 分 |
MONTH | 月(从0开始,可以+1使用) |
SECOND | 秒 |
DAY_OF_WEEK | 周中的天(周几,周日为1,可以-1使用) |
get/set方法,得到或者设置日历类里面的年月日等时间
get方法用来获取指定字段的值,set方法用来设置指定字段的值,代码使用演示:
import java.util.Calendar;
public class CalendarUtil {
public static void main(String[] args) {
// 创建Calendar对象
Calendar cal = Calendar.getInstance();
// 设置年
int year = cal.get(Calendar.YEAR);
// 设置月
int month = cal.get(Calendar.MONTH) + 1;
// 设置日
int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
System.out.print(year + "年" + month + "月" + dayOfMonth + "日");
}
}
import java.util.Calendar;
public class Demo07CalendarMethod {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 2020);
System.out.print(year + "年" + month + "月" + dayOfMonth + "日"); // 2020年1月17日
}
}
add方法修改,增加日历类里面的年月日等时间,包括传入负数,即让时间倒退
add方法可以对指定日历字段的值进行加减操作,如果第二个参数为正数则加上偏移量,如果为负数则减去偏移量。代码如:
import java.util.Calendar;
public class Demo08CalendarMethod {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
System.out.print(year + "年" + month + "月" + dayOfMonth + "日"); // 2018年1月17日
// 使用add方法
cal.add(Calendar.DAY_OF_MONTH, 2); // 加2天
cal.add(Calendar.YEAR, -3); // 减3年
System.out.print(year + "年" + month + "月" + dayOfMonth + "日"); // 2015年1月18日;
}
}
getTime方法
Calendar中的getTime方法并不是获取毫秒时刻,而是拿到对应的Date对象。
import java.util.Calendar;
import java.util.Date;
public class Demo09CalendarMethod {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
Date date = cal.getTime();
System.out.println(date); // Tue Jan 16 16:03:09 CST 2018
}
}
小贴士:
西方星期的开始为周日,中国为周一。
在Calendar类中,月份的表示是以0-11代表1-12月。
System类
java.lang.System
类中提供了大量的静态方法,可以获取与系统相关的信息或系统级操作,在System类的API文档中,常用的方法有:
public static long currentTimeMillis()
:返回以毫秒为单位的当前时间。public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
:将数组中指定的数据拷贝到另一个数组中。
1.1 currentTimeMillis方法
实际上,currentTimeMillis方法就是 获取当前系统时间与1970年01月01日00:00点之间的毫秒差值
import java.util.Date;
public class SystemDemo {
public static void main(String[] args) {
//获取当前时间毫秒值
System.out.println(System.currentTimeMillis()); // 1516090531144
}
}
1.2 arraycopy方法
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
:将数组中指定的数据拷贝到另一个数组中。
数组的拷贝动作是系统级的,性能很高。System.arraycopy方法具有5个参数,含义分别为:
参数序号 | 参数名称 | 参数类型 | 参数含义 |
---|---|---|---|
1 | src | Object | 源数组 |
2 | srcPos | int | 源数组索引起始位置 |
3 | dest | Object | 目标数组 |
4 | destPos | int | 目标数组索引起始位置 |
5 | length | int | 复制元素个数 |
包装类
1.1 概述
Java提供了两个类型系统,基本类型与引用类型,使用基本类型在于效率,然而很多情况,会创建对象使用,因为对象可以做更多的功能,如果想要我们的基本类型像对象一样操作,就可以使用基本类型对应的包装类,如下:
能够说出8种基本类型对应的包装类名称
基本类型 | 对应的包装类(引用数据类型)(位于java.lang包中) |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
1.2 装箱与拆箱
基本类型与对应的包装类(引用数据类型)对象之间,来回转换的过程称为”装箱“与”拆箱“:
-
装箱:把基本类型转换为对应的包装类对象。小的转大的
-
拆箱:把包装类对象转换为对应的基本类型。大的转小的
基本数值---->包装对象
Integer i = new Integer(666);//使用构造函数函数,装箱int-Integer,对象
Integer iii = Integer.valueOf(666);//使用包装类中的valueOf方法,装箱
包装对象---->基本数值
int num = i.intValue();//拆箱,Integer-int,拆箱
1.3自动装箱与自动拆箱
基本数据类型和其对应的引用数据类型互相转换,系统自动完成,jdk1.5开始支持,把基本数据类型自动变成其对应的引用数据类型叫做自动装箱,反过来就是自动拆箱
由于我们经常要做基本类型与包装类之间的转换,从Java 5(JDK 1.5)开始,基本类型与包装类的装箱、拆箱动作可以自动完成不要你来管。例如:
Integer i = 4;//自动装箱。相当于Integer i = Integer.valueOf(4);
//Integer i = null;// i.intValue(), null.intValue();//空指针异常NullPointerException
i = i + 5;//等号右边:将i对象转成基本数值(自动拆箱) i.intValue() + 5;//i=9;//Integer i = Integer.valueOf(9);
sout(i.toSting());//9
//加法运算完成后,再次装箱,把基本数值转成对象。自动装箱
1.4 基本类型与字符串之间的转换
基本类型转换为String
基本类型转换String方式有多种,这里只讲最简单的一种方式: 能够将基本类型转换为对应的字符串
基本类型直接与""相连接即可;如:34+""//"34" //能够将基本类型转换为对应的字符串666+""="666"
String转换成对应的基本类型 ,如下,能够将字符串转换为对应的基本类型
**除了Character类之外,**其他所有包装类都具有parseXxx静态方法可以将字符串参数转换为对应的基本类型:
public static byte parseByte(String s)
:将字符串参数转换为对应的byte基本类型,Byte类里面的方法public static short parseShort(String s)
:将字符串参数转换为对应的short基本类型。//Short类静态public static int parseInt(String s)
:将字符串参数转换为对应的int基本类型。//Integer.p(“3”)=3;public static long parseLong(String s)
:将字符串参数转换为对应的long基本类型。public static float parseFloat(String s)
:将字符串参数转换为对应的float基本类型。public static double parseDouble(String s)
:将字符串参数转换为对应的double基本类型。public static boolean parseBoolean(String s)
:将字符串参数转换为对应的boolean基本类型。
代码使用(仅以Integer类的静态方法parseXxx为例)如:
public class Demo18WrapperParse {
public static void main(String[] args) {//能够将字符串转换为对应的基本类型
int num = Integer.parseInt("100");//纯数字字符串前面可以加+或者-来表示正负数,并且不能超出int范围
}
}
注意:如果字符串参数的内容无法正确转换为对应的基本类型,则会抛出
java.lang.NumberFormatException
数字格式化异常,异常就是不正常,即这样做是个问题,得不到想要的结果
抽象类和接口区别:
抽象类:
- 抽象类使用abstract修饰;
- 抽象类不能实例化,即不能使用new关键字来实例化对象;
- 含有抽象方法(使用abstract关键字修饰的方法)的类是抽象类,必须使用abstract关键字修饰;
- 抽象类可以含有抽象方法,也可以不包含抽象方法,抽象类中可以有具体的方法;
- 如果一个子类实现了父类(抽象类)的所有抽象方法,那么该子类可以不必是抽象类,否则就是抽象类;
- 抽象类中的抽象方法只有方法体,没有具体实现;
接口:
- 接口使用interface修饰;
- 接口不能被实例化;
- 一个类只能继承一个类,但是可以实现多个接口;
- 接口中方法均为抽象方法;
- 接口中不能包含实例域或静态方法(静态方法必须实现,接口中方法是抽象方法,不能实现)
区别
- 抽象类可以提供成员方法的实现细节,而接口中没有;但是JDK1.8之后,在接口里面可以定义default方法,default方法里面是可以具备方法体的。
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型;
- 接口中每一个方法也是隐式指定为 public abstract,不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
ConcurrentHashMap
-
在ConcurrentHashMap中,无论是读操作还是写操作都能保证很高的性能
-
在进行读操作时(几乎)不需要加锁,而在写操作时通过锁分段技术只对所操作的段加锁而不影响客户端对其它段的访问。
-
在理想状态下,ConcurrentHashMap 可支持16个线程执行并发写操作,及任意数量线程的读操作。
-
关于它的存储结构
- JDK 1.7 中使用分段锁(ReentrantLock + Segment + HashEntry),相当于把一个 HashMap 分成多个段,每段分配一把锁,这样支持多线程访问。锁粒度:基于 Segment,包含多个 HashEntry。
- JDK 1.8 中使用 CAS + synchronized + Node + 红黑树。锁粒度:Node(首结点)(实现 Map.Entry<K,V>)。锁粒度降低了。
HashMap和HashTable的区别:
HashMap是线程不安全的非synchronized ,HashTable是线程安全的
HashMap可以接受null值,HashTable不行
HashMap的效率比HashTable高