JAVASE(上篇)详解
1. 常量和变量
public static void main(String[] args){
int age = 10;
//age = 20;
}
-
程序的运行过程
- 开辟内存空间(创建jum)
- 加载数据
- CPU提取数据运行
-
当数据加载到内存中时,我们按着运行期数据的是否变化把数据分成常量和变量
- 程序在运行期间,不断发生变化的量就是变量;
- 程序在运行期间,不发生变化的量就是常量。
-
生活中的常量:
- 整数的常量: 24小时,12月,60秒
- 小数常量:重力系数9.8, 圆周率3.14
- 字符串常量:“旺财”, “小强”
- 布尔类型的常量:true, false
2. 基本数据类型
一共有8中基本数据类型:(基本数据类型的默认值)
- 整数类型:数值类型中最常用的类型就是int,其次是long。
八位二进制位组成一个字节:
0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
---|
类型 | 占用存储空间 | 表数范围 |
---|---|---|
byte | 1字节 | -128 ~ 127 |
short | 2字节 | -215 ~ 215-1 |
int | 4字节 | -231 ~ 231-1 |
long | 8字节 | -263 ~ 263-1 |
- 小数类型:float和long比较由于存储结构不同,float比long表数的范围更大。
类型 | 占用存储空间 | 表数范围 |
---|---|---|
float | 4字节 | -3.403E38 ~ 3.403E38 |
double | 8字节 | -1.798E308 ~ 1.798E308 |
2.1. 基本数据类型的转换
2.1.1. 自动转换
- 转换规则:
- 所有的byte类型、short类型和char值在运算的时候都会转换成int类型,int的最大值是2147483647。
- 如果在计算中一个操作数据类型是long,另一个数的空间小于等于long,那么结果就是long
- 如果在计算中一个操作数据类型是float,另一个数的空间小于等于float,那么结果就是float
- 如果在计算中一个操作数据类型是double,另一个数的空间小于等于double,那么结果就是double
public static void main(String[] args){
//定义一个字节类型
byte b = 1;
//基本数据类型在计算的时候, 数值常量默认是int
int b1 = b + 1;
short b2 = 12;
//byte, short , int, char, 四种类型只要是发生计算都会自动的把结果提升成int类型
int b3 = b + b2;
//定义一个char类型, 字符和int类型可以自动的转换
char c = 'a';
//int i2 = c;
int i1 = c + 1;
System.out.println(i1);
//如果两个int类型计算会不会自动的提升类型呢?
int x = 2147483647;
int y = 12;
//☆☆☆int, long, float, double自身进行计算的时候没有类型的提升,如果超出范围了就会出现精度的损失
int z = x + y;
System.out.println(z);
//int, long, float, double不同的类型来计算的时候会向表数范围大的类型提升
float f = 1.3f;
//int类型和float类型计算转换成float
float f1 = f + y;
//小数的默认值是double
double d = f + 1.3;
// 12.5f是表示float类型的常量,表数范围小的常量或者变量可以自动的向表数范围大的类型转换
double d1 = 12.5f;
double d2 = y;
short s = 5;
//表数范围大的值不能自动的赋值表数范围小的变量
s = s + 5;
}
思考: byte b1 = 3,b2=4, b;
- b = b1 + b2; //报错,因为byte类型计算完毕后会自动提升为int类型。
- b= 4+3; //不会报错,因为int类型的常量相加只要没有达到byte的上限就可以。
2.1.2. 强制转换
当大容量类型的变量向小容量的类型转换的时候需要强制转换。
public static void main(String[] args){
int i = 999;
//数值类型转换的前提是精度不会损失
byte b = (byte)i;
System.out.println(b);
//定义long类型
long l = 10000;
int j = (int)l;
System.out.println(j);
//小数类型的强转
float f = 199.5f;
//☆☆☆转换成int, 小数转换成整数,就是把小数直接去掉
short i1 = (short)f;
System.out.println(i1);
//double向float转换
double d = 10.4;
float f1 = (float)d;
System.out.println(f1);
//字符类型的转换
char c = 'a';
int x = c;
System.out.println(x);
int y = 98;
char c1 = (char)y;
System.out.println(c1);
}
3. 运算符
3.1. 算数运算符
3.1.1. 四则运算:+,-,* ,/,%
public static void main(String[] args){
//定义两个整数类型变量
int a = 10;
int b = 3;
//+,-,*,/
int c = a + b;
System.out.println("a + b = "+ c);
int d = a - b;
System.out.println("a - b = "+ d);
int e = a * b;
System.out.println("a * b = "+ e);
//☆☆☆int直接做除法是整除
int f = a / b;
System.out.println("a / b = "+ f);
float f1 = 10.0f;
//由于f1是float类型b是int类型,在做计算的时候类型做了提升
float f2 = f1 / b;
System.out.println("f1 / b = "+ f2);
//取余数
int g = a % b;
System.out.println("a % b = "+ g);
}
3.1.2. 自增自减运算符
++、- - : 数值变量自身加1或者减一
- a++: 后加加
public static void main(String[] args){
//后++
int a = 1;
a++;
System.out.println(a); //2
int b = 1;
//如果后加加和使用这个后加加后的变量在一起运算,那么先使用的是之前的值,使用完后变量自身再加一
System.out.println(b++); //1
System.out.println(b); //2
}
- ++a: 前加加
public static void main(String[] args){
//前++
int a = 1;
++a;
System.out.println(a); //2
int b = 1;
//如果使用这个前加加的变量,那么使用的是加一之后的值。
System.out.println(++b); //2
}
- a–: 后减减
public static void main(String[] args){
//后--
int a = 1;
a--;
System.out.println(a); //0
int b = 1;
//如果后减减和使用这个后减减的变量在一起运算,那么先使用的是之前的值,然后使用变量自身减一后的值
System.out.println(b--); //1
System.out.println(b); //0
}
- –a: 前减减
public static void main(String[] args){
//前--
int a = 1;
--a;
System.out.println(a); //0
int b = 1;
//如果使用这个前加加的变量,那么使用的是减一之后的值。
System.out.println(--b); //0
}
- 练习:
public static void main(String[] args){
int a = 1;
int b = 2;
// 1 * 3 + 1 * 3
int c = (a++) * (++b) + (--a) * (b++);
System.out.println("a = " + a); //1
System.out.println("b = " + b); //4
System.out.println("c = " + c); //6
}
3.2. 逻辑运算符(&,|,&&, ||,!)
- &:多个表达式在计算的时候,若每一个表达式的计算结果都是true,则整体结果是true
表达式1(数据是否及格) | 表达式2(语文是否及格) | 结果 |
---|---|---|
True | True | True |
False | True | False |
True | False | False |
False | False | False |
- |:多个表达式在计算的时候,表达式的计算结果只要有一个表达式是true,则整体结果是true
表达式1(数据是否及格) | 表达式2(语文是否及格) | 结果 |
---|---|---|
True | True | True |
False | True | True |
True | False | True |
False | False | False |
- &&和&的区别:运行效果是相同的,但运行机制不同
public static void main(String[] args){
int math = 45;
int china = 23;
//单&号不够智能,即使前面的表达式的计算结果能决定总体结果,后面的表达式依然要计算
//System.out.println((math >= 60) & (++china >= 60));
//&&号为短路与,前面的表达式的计算结果如果能决定总体结果,后面的表达式则不计算(提高性能)
System.out.println((math >= 60) && (++china >= 60));
System.out.println(china);
}
- ||和|的区别:运行效果是相同的,但运行机制不同
public static void main(String[] args){
int math = 88;
int china = 23;
//单|号不够智能,即使前面的表达式的计算结果能决定总体结果,后面的表达式依然要计算
//System.out.println((math >= 60) | (++china >= 60));
//||是短路或,若前面的表达式的计算结果能决定总体结果,后面的则不计算
System.out.println((math >= 60) || (++china >= 60));
System.out.println(china);
}
4. 程序的流程控制
4.1. if
语法:if(条件判断表达式) {
执行语句
}
注意:
- 如果条件判断的表达式返回值是true,则可以执行if内部的语句,如果是false则不执行。
- 条件表达式的结果一定是true或者false。
示例: 实现程序如果分数大于60分就及格:
public static void main(String[] args){
//定义一个分数的变量
int score = 66;
if(score > 60){
System.out.println("恭喜了,及格了");
System.out.println("恭喜了,及格了");
System.out.println("恭喜了,及格了");
}
System.out.println("程序结束");
}
4.2. if…else
语法:if(条件判断表达式){
执行语句
}else{
执行语句
}
示例: 实现程序如果分数大于60分就及格:
public static void main(String[] args){
//定义一个分数的变量
int score = 56;
if(score > 60){
System.out.println("恭喜了,及格了");
}else{
System.out.println("抱歉,你没及格");
}
System.out.println("程序结束");
}
练习: 打印每个员工的工资等级,如果大于3000就是D级,如果大于5000是C级,如果大于8000是B级,如果大于10000是A级。
4.3. if….else if…else
语法:if(条件判断表达式){
执行语句
}else if(条件表达式1){
执行语句
}else if(条件表达式2){
…..
}else{
…..
}
注意: 多重if当遇到第一个满足的表达式条件时执行当前的if语句,就不会再向下去执行。
/**
示例:打印每个员工的工资等级,如果大于3000就是D级,
如果大于5000是C级,如果大于8000是B级,如果大于10000是A级。
D C B A
3000-5000 5000-8000 8000-10000 10000-
*/
public static void main(String[] args){
//定义一个工资的变量
int salary = 12000;
if(salary > 10000){
System.out.println("A");
}else if(salary > 8000){
System.out.println("B");
}else if(salary > 5000){
System.out.println("C");
}else if(salary > 3000){
System.out.println("D");
}
System.out.println("程序结束");
}
4.4. switch
swtich的表达式只能是byte, short, char, int,
语法:String</font><br/>
switch(表达式){
case 常量1:
执行语句1;
break;
case 常量2:
执行语句2;
break;
…..
default:
执行语句 ;
break;
}
示例: 根据工资级别打印不同的☆:
/**
根据工资级别打印不同的☆
工资的等级
1 2 3 4
☆ ☆☆ ☆☆☆ ☆☆☆☆
*/
public static void main(String[] args){
//定义一个等级的变量
int grade = 2;
//switch的表达式的类型和常量的类型要匹配并且都是byte, short, char, int,(jdk1.7之前) String(jdk1.7之后)类型
switch(grade){
//如果grade的值和 1相等的话就会执行System.out.println("☆");
case 1:
System.out.println("☆");
break; //跳出switch
case 2:
System.out.println("☆☆");
//break; //跳出switch, 如果没有break就会穿透执行,后面的case即使不匹配也会执行,直到遇到一个break才会跳出switch
case 3:
System.out.println("☆☆☆");
//break; //跳出switch
case 4:
System.out.println("☆☆☆☆");
break; //跳出switch
default:
System.out.println("无级别");
break;
}
System.out.println("程序结束");
}
switch和多重if的区别:
- if 可以做等值判断也可以做区间判断。
- switch只能做等值判断,不能做区间判断
练习:打印指定年月的天数。
4.5. 三元运算符
语法:逻辑表达式2?表达式3:表达式4
注意:
- ?前面的逻辑表达式2如果运算的结果是true,整个表达式的结果就是表达式3
- ?前面的逻辑表达式2如果运算的结果是false,整个表达式的结果就是表达式
- 三元运算符适合做两个值的判断
/**
给定一个变量 1 男, 2, 女
*/
public static void main(String[] args){
//定义一个变量
int gender = 1;
/*
if(gender == 1){
System.out.println("男");
}else{
System.out.println("女");
}
*/
//三元运算适合来代替if else的形式
//逻辑表达式2?表达式3:表达式4 ,三元运算符的结果是由表达式的结果的类型决定, 表达式三和表达式四结果类型要一致
char c = gender == 1?'男':'女';
System.out.println(c);
String man = "我是男";
String result = gender == 1? man:"我是女";
System.out.println(result);
//如果分数大于等于90A, 如果大于等于60B, 小于60C
int score = 99;
/*
if(score >= 90){
System.out.println("A");
}else if(score >= 60){
System.out.println("B");
}else{
System.out.println("C");
}
*/
String result1 = score >= 90? "A":(score >= 60 ? "B":"C");
System.out.println(result1);
}
4.6. 条件循环
4.6.1 while
语法:while(表达式){
循环体
}
表达式结果如果是true,那么就执行循环体,如果是false就结束循环。
示例: 打印1到100之间的所有的偶数和:
/**
打印1到100之间的所有的偶数和
*/
public static void main(String[] args){
//定义一个计数器
int i = 1;
//定义一个存储结果的变量
int total = 0;
while(i <= 100){
//判断是否是偶数
if(i % 2 == 0){
total += i;
}
//计数器的累加
i++;
}
System.out.println("结果是:"+ total);
}
**练习:**打印1到100之间的数字。
4.6.2 do…while
语法:do{
循环体
} while(表达式)
注意: do…while和while的区别,前者是先执行循环体,后者是先判断在执行循环体。
示例: 打印100到1000之间的所有夹心饼数。
/**
需求:打印100到1000之间的所有夹心饼数。
如:191,838
分析:
循环条件:判断计数器是大于等于100小于1000
循环操作:更新计数器,判断是否是夹心饼干
*/
public static void main(String[] args){
//定义一个计数器
int i = 100;
do{
//获得个位
int g = i % 10;
//获得百位
int b = i / 100;
if(g == b){
System.out.println(i);
}
//计数器的累加
i++;
}while(i < 1000);
}
练习: 打印100到1000之间的所有水仙花数。如:234 = (2)3+(3)3+(4)3
分析:
- 循环条件:判断计数器是大于等于100小于1000
- 循环操作:更新计数器,判断是否是水仙花数。
4.6.3 for
语法:for(表达式1; 表达式2; 表达式3){
循环体
}
注意:
- 表达式1:计数器的初始化,它只初始化一次;
- 表达式2:循环条件的判断,多次执行;
- 表达式3:修改计数器,多次执行。
练习1: 打印1到100之间的所有的偶数和
分析:
- 循环条件:判断计数器是否小于等于100
- 循环操作:累加偶数和,更新计数器
练习2: 打印5行5列 的☆
分析:
- 我们要使用2个循环
- 循环条件:判断计数器是否小于等于100
- 循环操作:累加偶数和,更新计数器
练习2: 打印乘法口诀
分析:
4.7. break
break用来跳出循环和switch。
class Demo{
public static void main(String[] args){
/**
需求:运动会上跑5000米,跑到第三圈时,抽筋了退赛
*/
for(int i = 1; i <= 10; i++){
if(i == 3){
System.out.println("抽筋了退出");
//跳出循环
break;
}
System.out.println("我跑到第"+i+"圈");
}
}
}
4.8. continue
continue用来控制循环,在某次循环的时候放弃,后续的接着循环。
class Demo{
public static void main(String[] args){
/**
需求:一周上五天班,然后我周三请假一天
*/
for(int i = 1; i <= 5; i++){
if(i == 3){
System.out.println("周三肚子疼请假");
//跳出本次循环,接着下一次循环
continue;
}
System.out.println("今天星期"+i+"上班");
}
}
}
5. 函数(方法)
函数(方法)是一段能完成独立功能的代码块。我们只需要写一次方法,就可以被多次调用,提高了代码的复用性。
语法:<public> <static> 返回值类型[void] 方法名([数据类型 变量名, 数据类型1 变量名1,…..]){
//方法体
[return 结果值];
}
特点:
- ⟨public⟩ ⟨static⟩ :后续详解;
- 返回值类型[void]:方法给调用者返回的数据类型,如果方法没有返回值,我们就使用void;
- 如果方法的返回值是void,那么return;是代表程序的终止(可以省略不写),如果方法要是有返回值那么就不能有return;
- 方法名: 见名之意,使用驼峰模式来规范方法名:xxxYyyZzz;
- 参数列表:方法的参数可有可无,参数列表中可以有多个参数,先指定参数数据类型,再指定参数的变量名,多个参数用逗号分隔;
- return:在方法有返回值的情况,程序返回的值一定要和返回值类型匹配。
误区: 要区分好变量的作用域问题
5.1. 方法的重载
在同一个类中,函数(方法)名相同,参数列表不一样(参数个数不一样,或者对应索引位类型不一样)的两个方法是重载关系,跟返回值没有一点关系。
目的: 为了节约方法的功能命名空间和提高代码的可读性。
class Demo{
public static void main(String[] args){
int a = 10;
int b = 19;
int result = add(a, b);
System.out.println("调用的是2个参数求和的方法: " + result);
int c = 20;
int result1 = add(a, b, c);
System.out.println("调用的是3个参数求和的方法: " + result1);
double result2 = add(a, 12.5);
System.out.println("调用的是2个参数(第二个参数是double类型)求和的方法: " + result2);
}
public static int add(int a, int b){
return a + b;
}
public static int add(int a, int b, int c){
return a + b + c;
}
public static double add(int a, double b){
return a + b;
}
}
5.2. 方法的重写(override)
子类继承父类时,子类的方法和父类的方法相同(访问修饰限制符, 返回值类型, 方法名, 参数列表),方法体不同。这种子类的方法将父类的方法“覆盖”的设计叫做重写。
class Person{
String name;
public void ridHorse(){
System.out.println(name + "骑马去上学");
}
}
class Hero extends Person{
/**
子类方法覆写(不是真正的覆盖)了父类的方法
*/
public void ridHorse(){
System.out.println(name + "骑马去作战");
}
public void method(){
this.ridHorse();
super.ridHorse();
}
}
public class Demo6{
public static void main(String[] args){
Hero h = new Hero();
h.name = "吕奉先";
h.method();
}
}
重写和重载的区别:
- 重写(override):在父子类的继承中有相同的方法,唯一不同就是方法体,一般是父类的该方法满足不了子类的需求所以才发生重写。
- 重载(overload):重载是在同一个类中,有着相同的方法名但是参数的数据类型或者参数的个数不同这两个方法就是重载。重载的目的:节省类中的命名资源和提高代码的可读性。
注意: 重写单独看不出来有什么特殊之处,但如果一旦结合多态,无敌了!
6. 数组
数据类型[] 数组名 = new 数据类型[整数]; //整数:数组的长度
画图理解:
基本数据类型的变量都存储在栈中,栈的特点是存储空间小,但是存取速度快,先进后出。
引用数据类型所占的空间比较大,存储在堆中,堆的特点,空间大,存取的速度比较慢。
数组定义的三种方式:
数据类型[] 数组变量名 = new 数据类型[]
int[] arr = new int[8];
arr是数组类型的变量(如果是引用数据类型习惯上叫引用), new int[8]真正的数据数据存储在堆中,新创建的数据每一个位置上的值都是其所属数据类型的默认值,数组的索引是从0开始。数据类型[] 数组变量名 = {值1, 值2,……}
int [] array = {12, 34, 56,……};
数组的长度由value的数量来决定。数据类型[] 数组变量名 = new 数据类型[]{值1,值2,值3,……}
数组的长度由value的数量来决定。
6.1. 冒泡排序
冒泡排序(Bubble sort):相邻的两个数逐个的做比较,如果前一个数比后一个数小那么就交换过来,当第一轮比较完毕后最小的值一定产生在末尾。
轮数:数组的长度 – 1
每一轮的比较次数: 数组的长度 – 轮号 – 1
6.2. 数组倒置
获得中间索引:length/2
获得前半段的对称索引:length – 1 - i
6.3. 二维数组
二维数组就是集合中套集合。
数据类型[][] 数组名 = new 数据类型[整数][整数]
注意:必须明确外围数组名
数据类型[][] 数组名 = {{值1,值2,值3},{值4,值5,值6}, ,……}
Int[][] array1 = new int[3][4];
int[][] array = {{1,2,3},{4,5,6},{8,3,6},……}
7. 类的概念
把具有相同行为和属性的一类事物抽象成类。
注意:类是概念,对象才是真正的实例。
-
学生类:
- 属性:学号,年龄,姓名
- 行为:学习,活动,吃饭
-
狗类:
- 属性:品种,颜色,性别
- 行为:看家,吃饭
7.1. 类的定义
语法:class 类名{
}
特点:
- class前面可以不加public,类名和文件名可以随意;
- 一个java文件中可以有多个类,但是只能有一个public的主类;
- 主类一定要和文件名一致;
- 学习阶段一个文件中一般写多个类,但在工作中一个文件一般都是一个类;
- 类的名字一定要见名之意,不要使用拼音,要用英文,千万不要使用中文;
- 类的名字首字母要大写,如果多个单词组成一个类名,则每个单词首字母都要大写。
7.2. 类的属性
7.2.1. 属性的概念
现在说的所有属性都是对象属性。
属性的特点:
- 属性(成员变量)要写在类中,而且是类的直接下级;
- 格式:
数据类型 属性名;
可以不用赋值,属性是有默认值的; - 对象的属性,后续的访问也都是通过对象来访问的;
- 属性的访问:
对象变量.属性名
。
7.2.2. 属性的默认值
数据类型 | 默认值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0 |
float | 0.0 |
double | 0.0 |
char | ‘ ’ |
boolean | false |
引用数据类型(数组,类,接口) | Null |
8. 对象的概念
对象是类的一个具体的实例。
类 | 对象 |
---|---|
学生类 | 小明,李雷,韩梅梅 |
狗类 | 旺财,栗子黄,二哈 |
… | … |
在内存中创建对象:
- 对象都是存储在堆中,然后在栈中存储局部变量,这个变量所存储的值就是想要的堆内存中的地址。
- 当使用new 创建的两个对象时会开辟两个不同的内存空间所有他们是相互隔离的。
- 我们可以在栈内存中创建两个变量都指向都一个堆内存中的地址,所以其中一个变量修改了堆内存中的数据,另一个变量就会读取到修改后的数据。
8.1. 对象的创建
语法:类名(数据类型) 变量名 = new 类名();
class Dog{
//狗名
String name;
//年龄
int age;
//品种
String type;
}
public class Demo{
public static void main(String[] args){
//创建对象
Dog d = new Dog();
//给属性赋值
d.name = "栗子黄";
d.age = 1;
d.type = "八哥";
System.out.println(d.name);
System.out.println(d.age);
System.out.println(d.type);
System.out.println("----------------------------");
Dog d1 = new Dog();
d1.name = "旺财";
d1.age = 2;
d1.type = "吉娃娃";
System.out.println(d1.name);
System.out.println(d1.age);
System.out.println(d1.type);
}
}
8.2. 创建对象的内存结构
学习面向对象最好的方式就是画内存结构,一定要重视。
注意:堆中的对象里面的属性值是隔离的。
8.3. JVM分区
- 栈内存:空间小,存取速度快,存储的都是局部变量,程序要想运行都需要进栈,先进后出。
- 堆内存:空间大,存取速度慢,存储对象和数组等引用数据类型数据。
- 数据共享区:用于存储数据共享的数据。例如class,字符串,函数方法。
8.4. 对象方法
语法:public 返回值类型 method(参数列表){
//方法体
return 返回值
}
对象方法:描述的是一个类的行为,例如:看家,吃饭,睡觉
特点:
- 对象方法定义在类中;
- 对象方法必须要使用对象来调用;
对象名.方法名(实参…)
- 方法是当前类的每一个对象共享的,它随着class文件加载到jvm而存储在数据共享区中。
class Dog{
//狗名
String name;
//年龄
int age;
//品种
String type;
/**
行为用方法来表示
*/
public void protectHome(){
System.out.println("狗狗在看家");
}
public void eat(){
System.out.println(name+"狗狗在吃食");
}
}
public class Demo1{
public static void main(String[] args){
//创建对象
Dog d = new Dog();
//给属性赋值
d.name = "栗子黄";
d.age = 1;
d.type = "八哥";
d.eat();
//创建对象
Dog d1 = new Dog();
//给属性赋值
d1.name = "旺旺";
d1.age = 2;
d1.type = "吉娃娃";
d1.eat();
}
}
8.5. 匿名对象
匿名对象就是创建了一个对象在栈中没有引用,这样的对象无法二次使用,马上就会被垃圾回收器回收。
注意: 不要写匿名对象。
public class Demo{
public static void main(String[] args){
//创建一个匿名对象,并且赋值name
new Dog().name = "旺财";
new Dog().age = 1;
}
}
9. 局部变量
- 局部变量的定义位置:在方法中或者语句块中;
- 变量一定要赋初始值,否则使用该变量的时候会报错;
- 在同一个作用域内变量名不能重复;
- 变量的作用范围:只在当前的大括号内有效(包括子括号),大括号外的所有程序都不能使用到该变量,所以这种变量也称为局部变量。
定义在方法中或者语句块中的变量都是局部变量,下面的代码里面都是局部变量。
public class Demo3{
static{
double d = 10.0;
}
{
float f = 3.14f;
}
public static void main(String[] args){
int i = 10;
Dog d = new Dog();
/**
语句块中的变量是局部变量
*/
for(int i = 0; i < 10; i++){
}
if(i > 10){
int a = 10;
}
}
/**
形参也是局部变量
*/
public static void add(int a, int b){
}
}
局部变量的特点:
- 作用范围:只限于所定义的大括号内;
- 局部变量是存储在栈中的;
- 局部变量没有默认值;
- 局部变量的生命周期:执行到这行局部变量代码的时候产生,所属的大括号结尾时结束。
public class Demo3{
public static void main(String[] args){
int i = 10;
Dog d = new Dog();
add(20);
}
public static void add(int a){
System.out.println(a + 10);
}
}
10. 成员变量(属性)
除了属性以外全是局部变量
属性的特点:
- 作用范围:属性的范围根据权限修饰符来决定(后面学);
- 位置:在堆中的对象里面;
- 默认值:有;
- 生命周期:对象中的属性是对象创建的时候才产生,如果它所属的对象被垃圾回收器回收(大括号结束, 引用变成null)了,属性就自然消失了。
11. 面向对象的特征之封装
封装就是把属性私有化,然后属性的权限可以自己来控制哪些属性可以被访问。
目的: 提高数据安全性,通过封装,可以实现对属性的访问权限控制,同时增加了程序的可维护性。
封装的步骤:
- 把属性私有化,这样的属性只能在本类中访问;
- 对每一个私有的属性,提供一对setter和getter方法,供外面使用;
- 创建对象,调用setter和getter方法来访问私有属性。
class Girl{
private String name;
private int age;
private String tel;
/**
针对tel提供的setter方法
*/
public void setTel(String mytel){
tel = mytel;
}
/**
针对tel提供getter方法
*/
public String getTel(){
return tel;
}
public void setAge(int myage){
age = myage;
}
public int getAge(){
return age;
}
public void setName(String myname){
name = myname;
}
public String getName(){
return name;
}
public void eat(){
System.out.println(name+"吃饭");
}
public void sleep(){
System.out.println(name+"在睡觉");
}
public void info(){
System.out.println("姓名:"+name+" 年龄:"+age+" 电话:"+tel);
}
}
public class Demo8{
public static void main(String[] args){
Girl g = new Girl();
g.setTel("123456789");
g.setName("莎莎");
g.setAge(19);
g.info();
}
}
12. this关键字
12.1. 在对象方法中用于指代“调用者”
我们发现main方法中打印的地址和eat方法中打印的地址是相同的:
内存结构:
总结: 哪个对象调用这个eat方法,那么这个方法中的this就是这个对象的引用。
注意:
- this只能在对象方法中(不带static的方法)
- 对象方法可以直接调用,只不过是省略了this.
12.2. 在对象方法中用于区分同名的变量
class Girl{
private String name;
private int age;
private String tel;
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
/**
* 在对象方法中用于区分同名的变量
*/
public void setTel(String tel){
this.tel = tel;
}
public String getTel(){
return tel;
}
}
public class Demo12{
public static void main(String[] args){
Girl g = new Girl();
g.setTel("13234234");
System.out.println(g.getTel());
}
}
12.3. 在构造器中
讲到构造器的时候再说。
13. 构造器
构造器就是创建对象和初始化属性的一个方法。
构造方法的特征:
- 构造器本身是一个方法,没有返回值,也没有void;
- 构造器的方法名必须和类名保持一致;
- 在方法中定义要初始化的参数列表。
13.1. 默认构造器
默认构造器的特征:
- 默认构造器是没有参数的。
- 默认构造器是隐藏存在的。
public Girl(){
System.out.println("默认构造器创建了一个girl的对象");
}
13.2. 有参构造器
如果类中有带有参数的构造器,那么隐藏的默认构造器就会被覆盖,如果这时想使用默认构造器,那么就需要显式的定义出来。
/**
* 默认构造器
*/
public Girl(){
System.out.println("默认构造器创建了一个girl的对象");
}
/**
* 使用有参构造器在创建对象的同时,还能初始化属性。
*/
public Girl(String name, int age){
this.name = name;
this.age = age;
}
public Girl(String name, int age, String tel){
this.name = name;
this.age = age;
this.tel = tel;
}
13.3. 本类构造器的调用
调用方法: 通过this(…)来调用本类构造器。
注意:
- this(…)必须放在构造方法的第一行;
- this(…)的调用不会再创建对象,只是初始化对象。
public Girl(String name, int age){
this.name = name;
this.age = age;
}
public Girl(String name, int age, String tel){
this(name, age);
this.tel = tel;
}
14. static关键字
14.1. static修饰在属性上
被static关键字修饰的属性叫类属性,没有被static关键字修饰的是对象属性。
特征:
- static属性访问的语法:类名.类属性;
- 当Student.class加载到jvm的时候scount就初始化了;
- 类属性的初始化早于对象属性的初始化;
- 每一个类的对象可以共享static的类属性;
- static类属性的销毁是随着class字节码文件销毁而销毁。
- 内存结构:
14.2. static修饰在方法上
特征:
- static的类方法是通过类名来访问的。也是可以通过对象访问(不推荐);
- static的方法是class文件加载到jvm的时候就直接可用了,对象方法必须要创建对象之后才能去调用;
- static的类方法不能访问对象属性和对象方法,只能访问类属性和类方法。static的方法只能访问static的东西;
- 对象方法什么都能调用。
class Student{
//姓名
String name;
//年龄
int age;
//学生数量
static int scount;
public static void printStudentCount(){
System.out.println(scount);
}
public void eat(){
System.out.println(name+"在吃饭");
}
public void info(){
System.out.println("姓名:"+name+" 年龄:"+age);
}
}
public class Demo17{
public static void main(String[] args){
Student.printStudentCount();
}
}
- 内存结构:
14.3. static代码块
语法:static{
}
特征:
- static代码块和static的属性一样,都是class文件加载到jvm的时候就执行的;
- 只要是使用了Student这个类,那么static的属性和代码块就会从上到下的按着顺序加载;
- 如果要想在static的代码块中来使用static的属性,那么必须要把属性定义在其上面;
- static代码块只能调用static的属性和方法。
class Student{
//姓名
String name;
//年龄
int age;
//学生数量
static int scount;
//定义一个静态代码块
static{
System.out.println(scount);
printStudentCount();
}
public static void printStudentCount(){
System.out.println("2");
}
public void eat(){
System.out.println(name+"在吃饭");
}
public static void main(String[] args){
}
}
public class Demo18{
public static void main(String[] args){
Student.printStudentCount();
}
}
总结: static代码块主要用于数据的初始化。
15. 设计模式之单例模式
设计模式是前人总结出来的经典的模式,单例模式是最简单的设计模式。
步骤:
- 提供一个私有化的默认构造器;
- 提供一个公有的类方法,返回该类(Singleton类)的对象,这个方法必须是类方法;
- 定义一个该类(Singleton类)的私有类属性;
- 在公有方法中实现单例。
懒汉模式:
class Singleton{
/**
定义一个Singleton的属性。
*/
private static Singleton s;
/**
把构造器私有化,使得构造器只能在本类中被访问。
*/
private Singleton(){
}
/**
提供一个对外的公有方法,返回一个Singleton的对象,用于外部获取单例对象。
*/
public static Singleton getInstance(){
if(s == null){
s = new Singleton();
}
return s;
}
}
public class SingletonTest{
public static void main(String[] args){
Singleton s = Singleton.getInstance();
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s);
System.out.println(s1);
System.out.println(s2);
}
}
饿汉模式:
class Singleton{
/**
定义一个Singleton的属性
*/
private static Singleton s = new Singleton();
/**
把构造器私有化
*/
private Singleton(){
}
/**
提供一个对外的公有方法,返回一个Singleton的对象
*/
public static Singleton getInstance(){
return s;
}
}
public class SingletonTest{
public static void main(String[] args){
Singleton s = Singleton.getInstance();
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s);
System.out.println(s1);
System.out.println(s2);
}
}
16. 类的继承
16.1. 类的继承的概念
语法:class 子类 extends 父类{
子类属性
子类方法
}
下面的代码设计不好,代码重复性太强:
继承的目的: 提高代码的复用性。
class Teacher{
String name;
int age;
String subject;
public void eat(){
System.out.println(name+"老师在吃饭");
}
}
class JavaTeacher extends Teacher{
public void teachJava(){
System.out.println(name+"老师在讲java");
}
}
class PHPTeacher extends Teacher{
String favor;
public void teachPHP(){
System.out.println(name+"老师在讲php");
}
}
public class Demo1{
public static void main(String[] args){
JavaTeacher jt = new JavaTeacher();
jt.name= "亮哥";
jt.age = 20;
jt.teachJava();
PHPTeacher pt = new PHPTeacher();
pt.name= "豹哥";
pt.age = 25;
pt.teachPHP();
}
}
16.2. 继承的特点
特征:
- 如果某个类中包含了某些类中的共同的属性和行为,我们就可以使用继承来设计程序。
- 如果采用继承的方式来设计程序,那么父类中的所有方法和行为都会继承下来(final,private除外),就相当于整个平移到了子类;
- 子类除了拥有通过使用extends关键字继承父类的共同属性,还可以有自己特有的属性或者方法;
- 父类更通用,子类更具体;
- 子类只能获得父类中的非private的属性,如果想要继承private属性就得提供公共的setter和getter方法。 私有的方法是无法继承下来的;
- java中类的继承只能是单继承。
class A{
}
class B extends A{
}
class C extends B{
}
public class Demo3{
public static void main(String[] args){
}
}
16.3. 继承的内存结构
父子类的继承关系:子类创建对象的时候,会在内部创建一个父类的super对象。
17. super关键字
17.1. 在构造器中
特点:
- 子类实例化的过程中父类的构造器先被调用,然后再调用子类的构造器;
- 子类构造器中默认存在一个super(), 这就是用于调用父类的默认构造器;
- 子类的任何构造器中如果没有调用super(…)的父类有参构造器,一定会调用父类的默认构造器,如果手动的调用父类有参构造器就不会再调用默认的super();
- super()在子类的构造器中调用必须是写在第一行。
class Teacher{
String name;
String subject;
public Teacher(){
System.out.println("父类对象被创建");
}
public Teacher(String name, String subject){
this.name = name;
this.subject = subject;
}
public void eat(){
System.out.println(name+"老师在吃饭");
}
}
class JavaTeacher extends Teacher{
int age;
public JavaTeacher(){
super();
System.out.println("子类对象被创建");
}
public JavaTeacher(String name, String subject, int age){
super(name, subject);
this.age = age;
}
public void teachJava(){
System.out.println(name+"老师在讲java");
}
public void info(){
System.out.println(name+" "+age+" "+subject);
}
}
public class Demo4{
public static void main(String[] args){
JavaTeacher jt = new JavaTeacher("亮哥", "java", 20);
jt.info();
}
}
17.2. 在子类的对象方法中
super在子类的对象方法中可以用来区分父子类同名的属性。
class Teacher{
String name = "李逵";
String subject;
public void eat(){
System.out.println(name+"老师在吃饭");
}
}
class JavaTeacher extends Teacher{
String name = "李鬼";
int age;
public void teachJava(){
System.out.println(this.name+"老师在讲java");
System.out.println(super.name+"老师在讲java");
}
}
public class Demo5{
public static void main(String[] args){
JavaTeacher jt = new JavaTeacher();
jt.teachJava();
}
}
17.3. 总结this和super关键字
this关键字:
- 代表当前类的的指定实例的引用
- 可以区分同名的属性和局部变量
- 通过this可以调用同类中的构造器(this(), this(参数列表))
- 调用本类里面的属性, this.属性名,this.方法()
super关键字:
- 代表当前类的父类的指定实例的引用
- 可以区分父子类中同名属性
- 通过super可以调用父类中的构造器(super(), super(参数列表))
- 调用父类里面的属性, super.属性名,super.方法()
18. final关键字
18.1. 在局部变量上
final关键字修饰的局部变量的值不能再被修改。
public static void main(String[] args){
final int a = 10;
a = 11;
}
18.2. 在对象属性上
在对象属性上加上final关键字可以直接赋值或者在构造器中来赋值,否则就会报错。
注意: 如果final修饰的属性没有主动赋值,那么这个类中的每一个构造器都要给这个属性赋初始值。
class Person{
String name;
final int age;
public Person(){
age = 10;
}
public Person(String name){
this.name = name;
age = 10;
}
}
public class Demo{
public static void main(String[] args){
Person p = new Person();
Person p1 = new Person("亮哥");
}
}
18.3. 在类属性上
final关键字修饰在类属性上,不能没有初始值(没有默认值),可以直接赋值,或者在static代码块(静态代码块)中赋值。
class Person{
String name;
final static int pcount; //没有默认值
static{
pcount = 10;
}
}
public class Demo{
public static void main(String[] args){
}
}
18.4. 在对象方法上
被final关键字修饰的方法不能在子类中重写。
class Person{
String name;
/**
final的方法不能在子类中重写
*/
public final void ridHorse(){
System.out.println(name+"骑马去约会");
}
}
class Hero extends Person{
public void ridHorse(){
System.out.println(name+"骑马去作战");
}
}
public class Demo{
public static void main(String[] args){
}
}
18.5. 在类上
被final关键字修饰的类不能被继承。
final class Person{
String name;
}
class Hero extends Person{
}
public class Demo{
public static void main(String[] args){
}
}
19. 抽象
当多个具体的实体类存在着共同的行为,但是有着不同的表现,我们在继承的过程中父类的方法具体实现不确定,但是能确定的是他们都有这种行为。
19.1. 抽象类
语法:abstract class 类名{
属性
方法
}
抽象类的特点:
- 抽象类不能实例化;
- 抽象类必须被子类继承后才能使用子类实例化;
- 继承了抽象类的子类(具体类),必须要实现抽象类的抽象方法;
- 如果子类是抽象类那么就不需要实现父抽象类的抽象方法;
- 抽象类的抽象方法不能和private,final, static共存。
java抽象类中能复有静态的抽象方法吗?
答:java抽象类中不能复有静态的抽象方法。
原因:抽象类是不能实例化的,即不能被分配内存;而static修饰的方法在类实例化之前就已经别分配了内存,这样一来矛盾就出现了:抽象类不能被分配内存,而static方法必须被分配内存。所以抽象类中不能有静态的抽象方法。
另外,定义抽象方法的目的是重写此方法,但如果定义成静态方法就不能被重写。
19.2. 抽象方法
语法:public abstract [返回值] [void] 方法名 (参数列表);
抽象方法的特点: 没有方法体,如果在一个类中存在一个抽象方法,那么这个类一定是抽象类。这个类上也要有abstract标识。
abstract class Teacher{
String name;
int age;
public void eat(){
System.out.println(name + "在吃饭");
}
/**
在子类中抽象出来了一个行为(没有实现),抽象的。
*/
public abstract void smoke();
}
class JavaTeacher extends Teacher{
public void teachJava(){
System.out.println(name + "教java");
}
public void smoke(){
System.out.println(name + "一边上课一边抽");
}
}
class UITeacher extends Teacher{
public void teachUI(){
System.out.println(name + "教UI");
}
public void smoke(){
System.out.println(name + "上完课程再抽");
}
}
public class Demo{
public static void main(String[] args){
}
}
20. 接口
20.1. 接口的概念
语法:interface 接口名{
抽象方法的定义;
….
}
接口: 如果抽象类中的所有方法都是抽象的,那么我们就可以把它定义为一个接口,接口是对行为的抽象,抽象类是对属性和行为的抽象。
注意: 接口不是类,没有属性。接口中都是抽象的方法,可以不写abstract。
interface Teacher{
public void eat();
public void smoke();
}
20.2. 接口的特征
- 接口不能实例化;
- 抽象类是天生要被继承,接口是天生要被实现;
- 具体类实现接口使用implements关键字;
- 具体类实现接口必须要实现接口中的所有抽象方法;
- 接口中不一定都是方法,也可以有常量,常量的命名的规范是每个字母都要大写,多个单词要用下划线分隔;
- 接口是可以多继承的,类只能是单继承。
20.3. 练习题
范例:使用接口的设计方式来计算圆和正方形的面积与周长。
interface Cal{
//在接口中定义圆周率的常量
public static final double PI = 3.14;
public double calArea();
public double calLong();
}
/**
定义一个圆的类
*/
class Circle implements Cal{
/**
半径
*/
private double r;
public Circle(double r){
this.r = r;
}
public double calArea(){
return PI * r *r;
}
public double calLong(){
return 2* PI * r;
}
}
class Rect implements Cal{
/**
边长
*/
private double r;
public Rect(double r){
this.r = r;
}
public double calArea(){
return r * r;
}
public double calLong(){
return 4 * r;
}
}
public class Demo5{
public static void main(String[] args){
Circle c = new Circle(10);
double ca = c.calArea();
double cl = c.calLong();
System.out.println("圆的面积:" + ca);
System.out.println("圆的周长:" + cl);
System.out.println("---------------------------");
Rect rect = new Rect(10);
double ra = rect.calArea();
double rl = rect.calLong();
System.out.println("正方形的面积:" + ra);
System.out.println("正方形的周长:" + rl);
}
}
20.4. 接口特性
jdk7之前接口中的方法都是抽象的。
jdk7之后接口中可以有非抽象的方法。
20.4.1. 接口中可以定义static的方法
interface Person{
/**
类中的static方法(类方法)都是通过类名调用的,
在接口中其实也行得通。
*/
public static void method(){
System.out.println("我是一个接口中的静态方法");
}
}
public class Demo6{
public static void main(String[] args){
Person.method();
}
}
20.4.2. 接口中可以提供默认方法
interface Person{
/**
默认方法在实现类中不需要强制实现 default关键字
*/
public default void method(){
System.out.println("我是接口的默认实现");
}
}
class PersonImpl implements Person{
public void method(){
System.out.println("实现类中实现了method的方法被执行");
}
}
public class Demo7{
public static void main(String[] args){
PersonImpl p = new PersonImpl();
p.method();
}
}
注意: 接口中的方法可以不写public,因为默认也是public的;接口中的所有方法都是public的。
interface Person{
void method();
}
class PersonImpl implements Person{
public void method(){
System.out.println("实现类中实现了method的方法被执行");
}
}
public class Demo8{
public static void main(String[] args){
PersonImpl p = new PersonImpl();
p.method();
}
}
21. 父子类型的转换
21.1. 子类转换父类
- 子类可以自动转换成父类;
- 转换后,父类的类型虽然是子类的真身,但是不能访问子类的特有属性和方法;
- 转换后,父类的方法在子类中有重写,通过父类的引用调用这个方法的时候调用的是子类的方法。
- 在具体类中:
class Teacher{
String name;
int age;
public void teach(){
System.out.println(name+"老师在讲课");
}
}
class JavaTeacher extends Teacher{
String favor;
public void playBall(){
System.out.println(name+"老师在玩球");
}
public void teach(){
System.out.println(name+"老师在讲java课");
}
}
public class Demo{
public static void main(String[] args){
/*
//创建子类对象
JavaTeacher jt = new JavaTeacher();
//子类可以自动的转换成父类
Teacher t = jt;
*/
Teacher t = new JavaTeacher();
t.name = "亮哥";
t.age = 20;
t.teach(); //调用的是子类的方法
}
}
- 在抽象类中:
abstract class Teacher{
String name;
int age;
public abstract void teach();
}
class JavaTeacher extends Teacher{
String favor;
public void playBall(){
System.out.println(name+"老师在玩球");
}
public void teach(){
System.out.println(name+"老师在讲java课");
}
}
public class Demo{
public static void main(String[] args){
Teacher t = new JavaTeacher();
t.name = "亮哥";
t.age = 20;
t.teach();
}
}
- 在接口中:
interface Teacher{
public void teach();
}
class JavaTeacher implements Teacher{
String name;
int age;
String favor;
public void playBall(){
System.out.println(name+"老师在玩球");
}
public void teach(){
System.out.println(name+"老师在讲java课");
}
}
public class Demo{
public static void main(String[] args){
JavaTeacher jt = new JavaTeacher();
jt.name = "亮哥";
jt.age = 20;
Teacher t = jt;
t.teach();
}
}
21.2. 父类转回子类
- 纯父类不能转换成子类;
- 只有父类的真身是子类,才能转回这个子类,否则不能转;
- 子类转换成父类的时候,曾经的子类是哪个,就只能转回这个子类,不能转换成别的子类;
- 如果现在只有父类的引用,但是想访问子类的特有属性和方法,就必须要转回子类。
class Teacher{
String name;
int age;
public void teach(){
System.out.println(name+"老师在讲课");
}
}
class JavaTeacher extends Teacher{
String favor;
public void playBall(){
System.out.println(name+"老师在玩球");
}
public void teach(){
System.out.println(name+"老师在讲java课");
}
}
class PHPTeacher extends Teacher{
public void teach(){
System.out.println(name+"老师在讲PHP课");
}
}
public class Demo{
public static void main(String[] args){
/*
Teacher t = new JavaTeacher();
JavaTeacher jt = (JavaTeacher)t;
jt.playBall();
Teacher t = new PHPTeacher();
PHPTeacher jt = (PHPTeacher)t;
*/
Teacher t = new JavaTeacher();
t.name = "亮哥";
t.age = 20;
//如果现在只有父类的引用,但是想访问子类的特有属性和方法,就必须要转回子类
JavaTeacher jt = (JavaTeacher)t;
jt.favor = "看电影";
jt.playBall();
}
}
在抽象类和接口同理。
22. 多态
多态就是行为具有表现多种功能的能力。
22.1. 继承多态具体类
class Teacher{
String name;
int age;
public void teach(){
}
}
class JavaTeacher extends Teacher{
public void teach(){
System.out.println(name + "老师在讲java课");
}
}
class PHPTeacher extends Teacher{
public void teach(){
System.out.println(name + "老师在讲PHP课");
}
}
class Leader{
/**
Teacher t = pt;
*/
public void check(Teacher t){
System.out.println("开始讲课");
t.teach();
System.out.println("打分");
}
}
public class Demo{
public static void main(String[] args){
JavaTeacher jt = new JavaTeacher();
jt.name = "张三";
jt.age = 30;
PHPTeacher pt = new PHPTeacher();
pt.name = "李四";
pt.age = 40;
Leader leader = new Leader();
leader.check(jt);
System.out.println("-----------------------");
leader.check(pt);
}
}
22.2. 继承多态抽象类
abstract class Teacher{
String name;
int age;
public abstract void teach();
}
class JavaTeacher extends Teacher{
public void teach(){
System.out.println(name+"老师在讲java课");
}
}
class PHPTeacher extends Teacher{
public void teach(){
System.out.println(name+"老师在讲PHP课");
}
}
class Leader{
/**
Teacher t = pt;
*/
public void check(Teacher t){
System.out.println("开始讲课");
t.teach();
System.out.println("打分");
}
}
public class Demo{
public static void main(String[] args){
JavaTeacher jt = new JavaTeacher();
jt.name = "张三";
jt.age = 30;
PHPTeacher pt = new PHPTeacher();
pt.name = "李四";
pt.age = 40;
Leader leader = new Leader();
leader.check(jt);
System.out.println("-----------------------");
leader.check(pt);
}
}
22.3. 接口多态
interface Teacher{
public void teach();
}
class JavaTeacher implements Teacher{
String name;
int age;
public void teach(){
System.out.println(name + "老师在讲java课");
}
}
class PHPTeacher implements Teacher{
String name;
int age;
public void teach(){
System.out.println(name + "老师在讲PHP课");
}
}
class Leader{
/**
Teacher t = pt;
*/
public void check(Teacher t){
System.out.println("开始讲课");
t.teach();
System.out.println("打分");
}
}
public class Demo{
public static void main(String[] args){
JavaTeacher jt = new JavaTeacher();
jt.name = "张三";
jt.age = 30;
PHPTeacher pt = new PHPTeacher();
pt.name = "李四";
pt.age = 40;
Leader leader = new Leader();
leader.check(jt);
System.out.println("-----------------------");
leader.check(pt);
}
}
22.4. instanceof的用法
class Leader{
public void check(Teacher t){
if(t instanceof JavaTeacher){
System.out.println("java老师开始讲课");
}
if(t instanceof PHPTeacher){
System.out.println("php老师开始讲课");
}
t.teach();
System.out.println("打分");
}
}
23. 包
概念: 就是文件夹;
作用: 对类做分类管理,可以区分同名不同包的类;
语法: package 包名(不同级别用“.”来分隔);如 package com.rl;位置放置文件的有效代码第一行(注释不算)
如何编译: javac –d . 源文件
访问权限:
- 相同包里面不同类的访问:可以访问对方的方法;
- 不同包里面不同类的访问:不同的包下,两个不同的类默认不能访问。如果要访问,需要import引入:import 包名.类名;
package com.rl;
public class ArrayUtil{
public static void main(String[] args){
int[] arr = {1,2,3,5,6,7};
printArr(arr);
}
public static void printArr(int[] arr){
for(int i = 0; i < arr.length ; i++){
System.out.print(arr[i]+"\t");
}
}
}
24. API的使用
我们到此为止学了java的规约,我们之前都是自己定义类,但是JDK也提供了很多类,我们需要使用API来学习这些类。
API中java.lang下的包都不需要引入就能直接使用。
25. 内部类
类中的类就是内部类
位置: 把一个类定义到另一个类中,那么内部的类就是内部类。
注意: 内部类不能直接创建。
创建内部类的语法: 外部类.内部类 变量名 = new 外部类对象.new内部类对象
内部类的外部类的方法如果想要访问内部类的方法,必须创建内部类的对象,根据内部类的对象来访问。
//外部类
class Outter{
int num;
public void method(){
System.out.println("我是一个外部类的方法");
}
//内部类
class Inner{
int n;
public void innerMeth(){
System.out.println("我是一个内部类的方法");
}
}
}
public class InnerTest{
public static void main(String[] args){
//外部类.内部类 变量名 = new 外部类对象.new内部类对象
Outter.Inner i = new Outter().new Inner();
i.innerMeth();
}
}
- 所属外部类中创建内部类对象:
class Outter{
int num;
public void method(){
System.out.println("我是一个外部类的方法");
//创建内部类的对象
Inner i = new Inner();
i.innerMeth();
}
class Inner{
int n;
public void innerMeth(){
System.out.println("我是一个内部类的方法");
}
}
}
public class InnerTest1{
public static void main(String[] args){
Outter o = new Outter();
o.method();
}
}
- 内部类访问所属的外部类的属性和方法:
class Outter{
int num = 2;
public void method(){
System.out.println("我是一个外部类的方法");
}
class Inner{
int n;
public void innerMeth(){
System.out.println("我是一个内部类的方法");
System.out.println(num);
method();
}
}
}
public class InnerTest2{
public static void main(String[] args){
Outter.Inner i = new Outter().new Inner();
i.innerMeth();
}
}
- 匿名内部类:
abstract class Teacher{
String name;
int age;
public abstract void teach();
}
/*
class JavaTeacher extends Teacher{
public void teach(){
System.out.println(name+"在讲课");
}
}
*/
public class InnerTest3{
public static void main(String[] args){
//这是一个继承了Teacher的匿名的内部类,new的恰恰就是这个没有名字的内部类的对象
Teacher t = new Teacher(){
public void teach(){
System.out.println(name+"在讲课---匿名内部类的方法");
}
};
t.name = "铁牛";
t.teach();
}
}
26. 访问权限的修饰符
访问权限的修饰符一共有四种:private, 默认, protected, public。能修饰:类,方法,属性。
26.1. 权限修饰符在类上
private | 默认 | protected | public | |
---|---|---|---|---|
类(非内部类) | N | Y | N | Y |
方法 | Y | Y | Y | Y |
属性 | Y | Y | Y | Y |
在类上的默认权限修饰符:只能在同一个包中可以访问。
在类上的public权限修饰符:在任何包中都可以访问。
注意:实战总是一个文件一个类,类上都是public。
26.2. 权限修饰符在方法上
作用域 | 同一个类 | 同包 | 子孙类 | 无关类 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
默认 | √ | √ | × | × |
private | √ | × | × | × |
注意:实战中方法99%都是public。
27. Object类
Object类是所有类的根类,所有的类都是直接或者间接的去继承Object类。
27.1. getClass()
返回此Object的运行时类。
class Person{
String name;
int age;
}
public class PersonTest{
public static void main(String[] args){
Person p = new Person();
/*说明tostring()方法是从Object继承下来的
String str = p.toString();
System.out.println(str);
*/
//获得当前的运行类
Class c = p.getClass();
//获得类的名字
String cName = c.getName();
System.out.println(cName);
}
}
27.2. hashCode()
返回对象的哈希码值。 由Object 类定义的 hashCode()方法会针对不同的对象返回不同的整数。
class Person{
String name;
int age;
}
public class PersonTest1{
public static void main(String[] args){
Person p = new Person();
int h1 = p.hashCode();
System.out.println(h1+"------>"+Integer.toHexString(h1));
Person p1 = new Person();
int h2 = p1.hashCode();
System.out.println(h2+"------>"+Integer.toHexString(h2));
Person p2 = new Person();
int h3 = p2.hashCode();
System.out.println(h3+"------>"+Integer.toHexString(h3));
}
}
27.3. toString()
我们打印一个对象的时候实际是打印的这个对象调用toString()的返回值。例如:Person@15db9742
- Object类源码中的toString():
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
class Person{
String name;
int age;
}
class Teacher{
}
public class PersonTest2{
public static void main(String[] args){
Person p = new Person();
System.out.println(p.toString());
Teacher t = new Teacher();
System.out.println(t);
}
}
27.4. 重写toString()
实战中一般都会重写toString()。
class Person{
String name;
int age;
public String toString() {
return "姓名:"+name+" 年龄:"+age;
}
}
27.5. finalize()
java的垃圾回收是不用开发人员关心的,后台有专门的垃圾回收的线程,会自动的来处理垃圾。
面试题:finalize, final, finally之间有什么区别?
27.6. equals(Object obj)
==:
基本类型:比较的是值。
引用类型:比较的是地址。
equals(Object obj) :
Object中的equals没有做什么只是做了判断两个对象的地址相等与否。
public boolean equals(Object obj) {
return (this == obj);
}
27.7. equals(Object obj) 的重写
Object类中的equals(Object obj)主要是留给子类重写的机会。
class Person{
int empNo;
String name;
int age;
public boolean equals(Object obj) {
//定义一个结果变量
boolean eq = false;
if(obj instanceof Person){
//把obj类型转回子类
Person p1 = (Person)obj;
if(this.empNo == p1.empNo && this.age == p1.age && this.name != null && this.name.equals(p1.name)){
eq = true;
}
}
return eq;
}
}
public class PersonTest3{
public static void main(String[] args){
Person p = new Person();
p.empNo = 100;
p.age = 20;
//p.name = "悟空";
Person p1 = new Person();
p1.empNo = 100;
p1.age = 20;
p1.name = "悟空";
System.out.println(p == p1);
System.out.println(p.equals(p1));
}
}
28. 异常
28.1. 异常体系
在我们写程序的时候难免会出现错误,java中的异常机制是为了提高我们程序的健壮性和容错性而存在。
28.2. 错误
Error类是throwable类的子类,程序在运行过程中出现的严重的问题,在运行过程中程序无法自己解决。
public class ErrorDemo {
public static void main(String[] args) {
List list = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
list.add(new byte[1024*1024*1024]);
}
}
}
错误都是以error为结尾。
28.3. 异常
异常是程序运行过程中可能产生的错误,可以通过程序来解决这种问题。
28.4. 异常的案例
public class ExceptionDemo {
public static void main(String[] args) {
int result = divide(10, 0);
System.out.println(result);
}
public static int divide(int a, int b){
int c = 0;
c = a/b;
return c;
}
}
第一行:异常的类的信息
第二行:被调用方法的发生异常的行数
第三行:调用端的方法发生异常的行数
28.5. 运行时的异常
编译的时候程序不报异常,只有在运行的时候才报异常。
RuntimeException类和他下面的子类都是运行时的异常。
28.6. 运行时异常的影响
- 错误代码在不做任何处理的情况下,jvm会根据实际情况创建出来一个异常对象,这个异常对象会自动的抛给调用端,这样调用端就有了这个异常对象,调用端在不做任何处理的情况下也是向上抛出。抛到最上层就到了jvm,jvm就为我们打印了这个异常的信息,所以我们能看到红色的异常信息。
- 被调用端发生异常后,后面的代码不能执行,调用端也是一样,发生异常的这行代码后面的代码也是不能执行的。
28.7. 运行时异常的处理
语法:try{
//放可能发生异常的代码
}catch(Exception e){
//e是当发生异常的时候来接收这个异常的对象
//只有报了异常才进入catch 做发生异常后的处理工作
}
28.7.1. 在被调用端处理
如果异常对象被catch了,就不会自动的向上抛了,执行catch里面的处理方案代码,后面的代码都会正常的执行。调用端由于没有接到任何的异常,后面的代码当然也会正常执行。
注意:实战中不推荐在被调用端来处理问题。
28.7.2. 在调用端处理(推荐)
public class ExceptionDemo {
public static void main(String[] args) {
int result = 0;
try {
result = divide(10, 0);
} catch (ArithmeticException e) {
System.out.println("收到错误通知,重新传参");
result = divide(10, 2);
}
System.out.println(result);
}
public static int divide(int a, int b){
int c = 0;
c = a/b;
return c;
}
}
28.7.3. 多个异常的处理
public class ExceptionDemo {
public static void main(String[] args) {
divide(10,2, new int[]{1,2,4});
}
public static void divide(int a, int b, int[] arr){
int c = 0;
try {
c = a/b;
System.out.println(arr[10]);
} catch (ArithmeticException e) {
System.out.println("被0除");
} catch (ArrayIndexOutOfBoundsException e){
System.out.println("数组越界");
}
}
}
public class ExceptionDemo {
public static void main(String[] args) {
divide(10,0, new int[]{1,2,4});
}
public static void divide(int a, int b, int[] arr){
int c = 0;
try {
c = a/b;
System.out.println(arr[10]);
} catch (Exception e) {
if(e instanceof ArithmeticException)
System.out.println("报了被0除异常");
if(e instanceof ArrayIndexOutOfBoundsException)
System.out.println("报了数组越界异常");
}
}
}
28.8. 异常对象
public class ExceptionDemo {
public static void main(String[] args) {
divide(10,0);
}
public static void divide(int a, int b){
int c = 0;
try {
c = a/b;
} catch (Exception e) {
/*
String message = e.getMessage();
System.out.println(message);
String s = e.toString();
System.out.println(s);
*/
e.printStackTrace();
}
}
}
28.9. finally代码块
finally语句块是最终要执行的代码块。
组合1:try{
}finally{
}
public class ExceptionDemo9 {
public static void main(String[] args) {
int result = divide(10, 0);
System.out.println(result);
}
public static int divide(int a, int b){
int c = 0;
try {
c = a/b;
} finally {
System.out.println("我是最终要执行的代码...");
}
System.out.println("finally后面的...");
return c;
}
}
组合2:try{
}catch(Exception e){
}finally{
}
public class ExceptionDemo10 {
public static void main(String[] args) {
divide(10,0);
}
public static void divide(int a, int b){
int c = 0;
Connection connection = null;
try {
//connection = xxx
c = a/b;
} catch (ArithmeticException e) {
System.out.println("被0除");
}finally {
//connection关闭
}
}
}
28.10. 编译期的异常
编译的时候程序报错,必须要处理才能继续。
jdk提供的异常除了RuntimeException类以及它下面的子类以外其余都是编译期的异常类。
public class ExceptionDemo {
public static void main(String[] args) throws FileNotFoundException {
readFile();
}
public static void readFile() throws FileNotFoundException {
/*try {
Reader r = new FileReader("D:\\a.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}*/
Reader r = new FileReader("D:\\a.txt");
}
}
编译期异常,要么处理,要么也向上抛,如果向上抛出,需要主动的在方法后面声明。
28.11. 自定义异常
在项目中我们会有自己定义的异常:
public class StockException extends RuntimeException {
public StockException() {
super();
}
public StockException(String message) {
super(message);
}
public StockException(String message, Throwable cause) {
super(message, cause);
}
public StockException(Throwable cause) {
super(cause);
}
protected StockException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
测试代码:
public class OrderService {
static int stock = 10;
public static void submitOrder(int num){
if(stock < num){
//创建异常,并且抛出
throw new StockException("库存不足"+num+"个");
}
System.out.println("购买成功");
}
public static void main(String[] args) {
try {
submitOrder(11);
} catch (Exception e) {
System.out.println("库存不足已经得知");
submitOrder(10);
}
}
}