Java基础
一、Java基础知识了解
1.JDK、JRE、JVM三者之间的关系
- JDK:Java开发工具箱
- JRE:java运行环境
- JVM:java虚拟机
JVM是不能独立安装的。
JRE和JDK都是可以独立安装的。
安装JDK的时候:JRE就自动安装了,同时JRE内部的JVM也就自动安装了。
安装JRE的时候:JVM也就自动安装了。(比如部署项目,只需要安装JRE就行,JRE体积小)
2.对Java的加载与执行的理解
java程序非常重要的两个阶段:
- 编译阶段:.java文件通过编译成.class文件,jvm才能识别
- 运行阶段:jvm执行.class文件
.class不是二进制文件,无法直接在操作系统运行
3.Java知识图解
4.计算机中的加减法
原码
虽然原码表示简单,但是原码在做加减法的时候,很麻烦!以4bit位为例:
1+(-1) = 0001 + 1001 = 怎么让计算机去计算?(虽然我们知道该去怎么算,但是计算机不知道!)
我们得创造一种更好的表示方式!于是我们引入了反码:
反码
- 正数的反码是其本身
- 负数的反码是在其原码的基础上, 符号位不变,其余各个位取反
经过上面的定义,我们再来进行加减法:
1+(-1) = 0001 + 1110 = 1111 => -0 (直接相加,这样就简单多了!)
思考:1111代表-0,0000代表+0,在我们实数的范围内,0有正负之分吗?
- 0既不是正数也不是负数,那么显然这样的表示依然不够合理!
补码
根据上面的问题,我们引入了最终的解决方案,那就是补码,定义如下:
- 正数的补码就是其本身 (不变!)
- 负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
其实现在就已经能够想通了,-0其实已经被消除了!我们再来看上面的运算:
1+(-1) = 0001 + 1111 = (1)0000 => +0 (现在无论你怎么算,也不会有-0了!)
所以现在,4bit位能够表示的范围是:-8~+7(Java使用的就是补码!)
二、Java基础语法
1.注释
单行注释://
多行注释:/_ _/
文档注释:/** */
待做标记(类似于书签)://TODO
2.Java关键字
Java的关键字一共有50个。
Java的三个特殊值:true, false,null。
3.Java标识符
标识符:给变量、类等取名的字符序列。
标识符命名规则:
- 只能有26个英文字母大小写,数字0-9,下划线_,美元符号$。
- 不能以数字开头。
- 不能包含空格。
- 不能直接使用关键字、保留字、特殊值。
- 严格区分大小写。
标识符命名规范:
- 见名知意
- 类名等:每一个单词的首字母大写,形式:XxxYyyZzz
- 变量名等:从第二个单词开始首字母大写,形式:xxxYyyZzz
- 常量名等:每一个字母都大写,单词之间可以使用下划线分割,形式:XXX_YYY_ZZZ
- 包名:每一个单词都是小写,单词之间使用.分割,形式:xxx.yyy.zzz
4.Java数据类型分类
基本数据类型(8种):byte,short,int,long,float,double,char,boolean。
引用数据类型:类、接口、数组、枚举、注解等
基本数据类型
数据类型 | 关键字 | 内存占用 | 取值范围 |
---|---|---|---|
整数 | byte | 1 | 负的2的7次方 ~ 2的7次方-1(-128~127) |
short | 2 | 负的2的15次方 ~ 2的15次方-1(-32768~32767) | |
int | 4 | 负的2的31次方 ~ 2的31次方-1 | |
long | 8 | 负的2的63次方 ~ 2的63次方-1 | |
浮点数 | float | 4 | 1.401298e-45 ~ 3.402823e+38 |
double | 8 | 4.9000000e-324 ~ 1.797693e+308 | |
字符 | char | 2 | 0-65535 |
布尔 | boolean | 1 | true,false |
在java中整数默认是int类型,浮点数默认是double类型。
int a = 10;
int b = 010;//八进制0
int c = 0x10;//0x开头十六进制 0-9 A-F
System.out.println(a);//10
System.out.println(b);//8
System.out.println(c);//16
//float 舍入误差
//double
float a = 2121233213412412f;
float b = a + 1;
System.out.println(a==b);//true
//最好完全避免使用浮点数进行比较
char a='a';
char b='中';
System.out.println(a);//a
System.out.println((int)a);//7
System.out.println(b);//中
System.out.println(((int)b));//20013
char c='\u0061';
System.out.println(c);//a
// \t 空格
//\n 换行
System.out.println("hello\tworld");
变量
数据类型 变量名 = 初始化值; // 声明变量并赋值
String name="zhangsan";
System.out.println(name);
- 定义和赋值两部分
- 他在内存里开辟了一片空间,可以存数据
- 他可以反复的到处使用
- 变量其实就是一个引用,他指向堆内存的一块区域,我们想操作一个对象只需要使用它的引用即可
使用变量时的注意事项
- 在同一对花括号中,变量名不能重复。
- 变量在使用之前,必须初始化(赋值)。
- 定义long类型的变量时,需要在整数的后面加L(大小写均可,建议大写)。因为整数默认是int类型,整数太大可能超出int范围。
- 定义float类型的变量时,需要在小数的后面加F(大小写均可,建议大写)。因为浮点数的默认类型是double, double的取值范围是大于float的,类型不兼容。
类型转换
整型里边小转大自动转,大转小需要强转,应为可能会丢失精度;
整型和浮点型:浮点型转整型需要强转,反之不需要,但是他以科学计数法表示可能会丢失精度;
char能不能和整型转: 就是两个字节,随便转,就能当成一个short。但是char类型的变量经过计算都是int。任何short,byte,int,char无论是逻辑运算还是数学运算结果都是int;
int a=10;
double b=11.5;
double c= a+b; //数据范围小的数值或者变量赋值给另一个表示数据范围大的变量,自动转
int d= (int) (a+b);//数据范围大的数值或者变量赋值给另一个表示数据范围小的变量,需强转
int a=128;
byte b= (byte) a;
System.out.println(b);//-128,内存溢出
5.运算符
5.1 逻辑运算符
位运算
位运算是将数据先转化为二进制数补码形式,再逐位(bit)按规则计算;
符 | 作用 | 说明 |
---|---|---|
& | 按位与 | a&b,a和b都是true,结果为true,否则为false |
| | 按位或 | a|b,a和b都是false,结果为false,否则为true |
~ | 按位非 | 0取反为1,1取反为0 |
^ | 按位异或 | a^b,a和b结果不同为true,相同为false |
//两个数转为二进制,然后从高位开始比较,如果相同则为0,不相同则为1。
int a=10;
int b=3;
int c=10;
System.out.println(a^b);//9
System.out.println(a^c);//0
逻辑运算符
逻辑运算又称布尔运算。常用0、1或者true、false来表示两者的关系;
符号 | 作用 |
---|---|
&& | 短路与 |
|| | 短路或 |
! | 逻辑非 |
在逻辑与运算中,只要有一个表达式的值为false,那么结果就可以判定为false了,没有必要将所有表达式的值都计算出来,短路与操作就有这样的效果,可以提高效率。同理在逻辑或运算中,一旦发现值为true,右边的表达式将不再参与运算。
- 逻辑与&,无论左边真假,右边都要执行。
- 短路与&&,如果左边为真,右边执行;如果左边为假,右边不执行。
- 逻辑或|,无论左边真假,右边都要执行。
- 短路或||,如果左边为假,右边执行;如果左边为真,右边不执行。
5.2 算数运算符
加 减 乘 除 取余(%)
int a = 10;
int b = 20;
System.out.println("" + a + b);//1020 先转成string
System.out.println(a+b+"");//30
5.3 赋值运算符
- ++:自增,变量的值加1
int x = 10;
int y = x++; // 赋值运算,++在后边,所以是使用x原来的值赋值给y,x本身自增1
System.out.println("x:" + x + ", y:" + y); // x:11,y:10
int m = 10;
int n = ++m; // 赋值运算,++在前边,所以是使用m自增后的值赋值给n,m本身自增1
System.out.println("m:" + m + ", m:" + m); // m:11,m:11
- –:自减,变量的值减1
- 赋值运算符的作用是将一个表达式的值赋给左边,左边必须是可修改的,不能是常量。
| 符号 | 作用 | 说明 |
| — | — | — |
| = | 赋值 | a=10,将10赋值给变量a |
| += | 加后赋值 | a+=b,a=a+b |
| -= | 减后赋值 | a-=b,a=a-b |
| = | 乘后赋值 | a=b,将a×b的值给a |
| /= | 除后赋值 | a/=b,将a÷b的商给a |
| %= | 取余后赋值 | a%=b,将a÷b的余数给a |
5.4 关系运算符
符号 | 说明 |
---|---|
== | a==b,判断a和b的值是否相等,成立为true,不成立为false |
!= | a!=b,判断a和b的值是否不相等,成立为true,不成立为false |
> | a>b,判断a是否大于b,成立为true,不成立为false |
>= | a>=b,判断a是否大于等于b,成立为true,不成立为false |
< | a<b,判断a是否小于b,成立为true,不成立为false |
<= | a<=b,判断a是否小于等于b,成立为true,不成立为false |
注意事项:
- 关系运算符的结果都是boolean类型,要么是true,要么是false;
- 千万不要把“”误写成“=”,"“是判断是否相等的关系,”="是赋值;
5.5 三元运算符
关系表达式 ? 表达式1 : 表达式2;
如果条件表达式成立或者满足则执行表达式1,否则执行第二个。
int a = 10;
int b = 20;
int c = a > b ? a : b; // 判断 a>b 是否为真,如果为真取a的值,如果为假,取b的值
6.流程控制语句
6.1 if语句(判断语句是就执行否就跳过)
if (i == 0) {}
if (i == 0) {} else {}
if (i == 0) {} else if (i == 1) {}
6.2 switch(case的后面不写break,将出现穿透现象)
//i可以是 byte short int String enum 的数据
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
switch (i){
case 1:
System.out.println("111");
break;
case 2:
System.out.println("222");
break;
default:
System.out.println("333");
break;
}
- switch(表达式)的值的类型,只能是:4种基本数据类型(byte,short,int,char),两种引用数据类型(JDK1.5之后枚举、JDK1.7之后String)
- case后面必须是常量值,而且不能重复
6.3 for
语法:
for (初始化语句;条件判断语句;条件控制语句) {
循环体语句;
}
for(int i=1; i<=5; i++) {
System.out.println(i);
}
6.4 while
while (i<5){i++;}//先判断,符合条件循环一次
do {i++;}while (i<5);//先执行,在判断
6.5 break
跳出循环,结束循环;
for循环可已打标签,使用【break + 标签名】可以 退出 被打标签的循环;
flag:
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
if (j > 0) {
break flag;
}
System.out.println("====" + j);
}
}
6.5 continue
跳过本次循环,继续下次循环;
7.数组
7.1 数组的定义
int[] nums = {1,2,3};
int[] nums = new int[3];
//类型[] 名字 = new 类型[长度];
数组中可以存储任意类型的数据,但是数组本身是引用类型的,创建数组对象会在内存中开辟一整块连续的空间,而数组名中引用的是这块连续空间的首地址
7.2 数组的性质
- 数组一旦建立不能改变长度;
- 每个位置只能存一个值,多了会覆盖;
- 编号从0开始,下标;
- 他有个长度的属性,最后一个位置的编号是长度-1;
- 数组里边可以是基本类型,也可以是引用类型;
7.3 数组的遍历
for (int i = 0; i < nums.length; i++) {
System.out.println(nums[i]);
}
//第二种增强for遍历
for (int i : nums) {
System.out.println(i);
}
7.4 Arrays工具类
- binarySearch(数组,key)-二分搜索
int[] arr = {2, 12, 6, 19, 8};
int index = Arrays.binarySearch(arr, 19);
System.out.println(index);//3;
- Arrays.sort(数组);
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));//[2, 6, 8, 12, 19]
- 数组的拷贝
int[] arr1 = Arrays.copyOf(arr, 3);
System.out.println(Arrays.toString(arr1));//[2, 6, 8]
- 数组深拷贝和浅拷贝
int[] nums = {10, 15, 33};
System.out.println(nums.toString());
int[] num1 = nums;
//浅拷贝:拷贝的是内存地址
for (int i = 0; i < num1.length; i++) {
System.out.println(num1.toString());
}
//深拷贝拷贝的是内容
int[] num2 = new int[nums.length];
for (int i = 0; i < num2.length; i++) {
int num = nums[i];
num2[i] = num;
System.out.println(num2.toString());
}
- 比较数组的内容
boolean equals = Arrays.equals(arr, arr1);
System.out.println(equals);//false
7.5 二维数组
类似于一个平面;
int[][] nums=new int[2][4];//[2]表示分2个一维数组,[4]表示从每个一维数组再分4个数字
nums[1][2]=2;
nums[2][4]=11;
8.方法
格式:
public static 返回值类型 方法名(参数) {
方法体;
return 数据 ;
}
名词 | 解释 |
---|---|
返回值类型 | 方法操作完毕之后返回的数据的数据类型;如果方法操作完毕,没有数据返回,这里写void,而且方法体中一般不写return; |
方法名 | 调用方法时候使用的标识; |
参数 | 由数据类型和变量名组成,多个参数之间用逗号隔开; |
方法体 | 完成功能的代码块; |
return | 如果方法操作完毕,有数据返回,用于把数据返回给调用者; |
三、面向对象
1、类的定义
① 定义类
② 编写类的成员变量
③ 编写类的成员方法
public class Student {
// 属性 : 姓名, 年龄
// 成员变量: 跟之前定义变量的格式一样, 只不过位置发生了改变, 类中方法外
String name;
int age;
// 行为 : 学习
// 成员方法: 跟之前定义方法的格式一样, 只不过去掉了static关键字.
public void study(){
System.out.println("学习");
}
}
- 创建对象的格式:类名 对象名 = new 类名();
- 调用成员的格式:对象名.成员变量;对象名.成员方法();
2、成员变量和局部变量的区别
- 成员变量(类中方法外)局部变量(方法内部或方法声明上)
- 成员变量(堆内存)局部变量(栈内存)
- 成员变量(随着对象的存在而存在,随着对象的消失而消失)局部变量(随着方法的调用而存在,随着方法的调用完毕而消失)
- 成员变量(有默认初始化值)局部变量(没有默认初始化值,必须先定义,赋值才能使用)
3、构造方法
- 方法名与类名相同,大小写也要一致
- 没有返回值类型,连void都没有
- 没有具体的返回值(不能由retrun带回结果数据)
- 不能手动调用构造方法
- 创建对象的时候调用,每创建一次对象,就会执行一次构造方法
**构造方法的作用:**用于给对象的数据(属性)进行初始化
**构造方法的创建:**如果没有定义构造方法,系统将给出一个默认的无参数构造方法如果定义了构造方法,系统将不再提供默认的构造方法
4、方法的重载
方法重载的概念:方法重载指同一个类中定义多个方法之间的关系,满足下列条件的多个方法相互构成重载
- 多个方法在同一个类中
- 多个方法具有相同的方法名
- 多个方法的参数不相同,类型不同或者数量不同
注意:
- 重载仅对对应方法的定义,与方法的调用无关,调用方式参照标准格式
- 重载仅针对同一个类中方法的名称与参数进行识别,与返回值无关,换句话说不能通过返回值来判定两个方法是否相互构成重载
public class Study {
public int studyTime;
//重载构造
public Study(){
}
public Study(int studyTime) {
this.studyTime = studyTime;
}
//重载方法
public void StudyTime(){
}
public void StudyTime(String studyTime){
}
}
5、封装
将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
成员变量private,提供对应的getXxx()/setXxx()方法
好处:通过方法来控制成员变量的操作,提高了代码的安全性
把代码用方法进行封装,提高了代码的复用性
5.1 权限修饰符
被private修饰的成员,只能在本类进行访问,针对private修饰的成员变量,如果需要被其他类使用, 提供相应的操作提供“get变量名()”方法,用于获取成员变量的值,方法用public修饰提供“set变量名(参数)”方法,用于设置成员变量的值,方法用public修饰
权限修饰符访问范围
5.2 this关键字
this修饰的变量用于指代成员变量,其主要作用是(区分局部变量和成员变量的重名问题)
- 方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量
- 方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量
6、继承
可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法
**实现继承的格式:**继承通过extends实现
格式:class 子类 extends 父类 { } ;举例:class Dog extends Animal { }
继承可以让类与类之间产生关系,子父类关系,产生子父类后,子类则可以使用父类中非私有的成员。
6.1 继承特点
- Java中类只支持单继承,不支持多继承
- Java中类支持多层继承
6.2 方法重写
发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法。
6.3 抽象类
1. 抽象方法定义
权限修饰符 abstract 返回值类型 方法名字(参数列表) ;
abstract关键字
抽象方法没有方法体, 不需要{},直接分号结束
当一个类中的方法是抽象方法的时候,这个类必须是抽象类,在类的关键字class前面使用abstract修饰.
public abstract class Animal {
/**
* 动物吃什么?
* 说不清楚,抽象,可以不说
*/
public abstract void eat();
}
2.抽象类的使用方式
- 抽象类不能实例化对象,不能new对象(为什么不能建立对象,类中有没有主体的方法存在,建立对象调用抽象方法是绝对的错误,因此不能建立对象)
- 需要子类继承抽象类,重写抽象方法
- 创建子类对象
- 使用多态性创建对象,调用方法执行子类的重写
public class Cat extends Animal{
/**
* 重写父类的方法
* 去掉修饰符 abstract
* 添加主体 {}
*/
public void eat(){
System.out.println("猫吃鱼");
}
}
public static void main(String[] args) {
//创建Animal的子类对象
Animal animal = new Cat();
//eat方法不可能执行父类,运行子类的重写
animal.eat();
}
3.抽象类中成员的定义
①抽象类中能否定义成员变量
可以定义成员变量,成员变量私有修饰,提供方法 get/set,由子类的对象使用
public abstract class Animal {
//抽象类中能否定义成员变量
private String name;
public abstract void eat();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) {
Animal animal = new Cat();
animal.eat();
//animal对象调用方法 get/ set
animal.setName("tom");
String name = animal.getName();
System.out.println(name);
}
②抽象类中有构造方法吗
抽象类中有构造方法,不写有默认的
public abstract class Animal {
public Animal(){
System.out.println("Animal的构造方法");
}
public Animal(String name){
this.name = name;
System.out.println("有参数String的构造方法");
}
//抽象类中能否定义成员变量
private String name;
public abstract void eat();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Cat extends Animal {
public Cat(){
//调用父类的有参数构造方法
super("张三");
}
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
③抽象中能否不定义抽象方法
抽象类中,可以不定义出抽象方法.
但是,如果有抽象方法存在,这个类必须是抽象类
④子类还是抽象类的问题
当一个子类继承一个抽象类的时候,子类必须重写全部的抽象方法.假如子类重写了部分抽象方法,这个子类依然还是抽象类.
public abstract class Animal {
public abstract void eat();
public abstract void sleep();
}
/**
* Cat继承父类Animal,Cat类拥有了父类的成员
* 父类有什么,我就有什么
*/
public abstract class Cat extends Animal {
public void eat(){}
/**
* 方法sleep没有重写
* 还是一个抽象的方法
*/
// public abstract void sleep();
}
6.4 接口
当一个抽象类中的所有方法全部是抽象的时候,可以将这个抽象类换一个更加贴切的名词,叫他接口. 接口是特殊的抽象类.
接口在编译后,依然还是.class文件
语法:public interface 接口名{}
1.接口中成员定义
- 成员变量
- 成员变量的定义是具有固定格式
- 成员变量的修饰符是固定 public static final
public static final 数据类型 变量名 = 值 ;
- 成员方法
- 成员方法的定义是具有固定格式
- 成员方法的修饰符固定为 public abstract
public abstract 返回值类型 方法名(参数列表) ;
2.接口的使用方式
- 接口不能建立对象,不能new
- 需要定义类,实现接口(继承类,在接口中称为实现,理解为继承)
- 实现接口,使用新的关键字 implements
- 实现的格式
class 类 implements 接口名{}
- 重写接口中的抽象方法
- 创建子类的对象
/**
* 定义好的接口
*/
public interface MyInterFace {
//接口的成员变量
public static final int A = 1;
//接口的成员方法
public abstract void myInter();
}
/**
* 定义MyInterFace接口的实现类
* 重写接口的抽象方法
*/
public class MyInterFaceImpl implements MyInterFace{
public void myInter(){
System.out.println("实现类实现接口,重写方法");
}
}
public static void main(String[] args) {
//创建对象,多态性,创建接口实现类的对象
MyInterFace my = new MyInterFaceImpl();
my.myInter();
//输出接口中的成员A的值
System.out.println(my.A);
}
3.接口的多实现
类和类之间单继承,局限性的问题.接口的出现,是对单继承的改良,允许一个类同时实现多个接口.
语法:class 类名 implements 接口A,接口B{}
实现类,重写实现的多有接口中的抽象方法
public interface A {
public abstract void a();
}
public interface B {
public abstract void b();
}
/**
* 实现接口A和B
*/
public class C implements A,B{
@Override
public void a() {
System.out.println("重写A接口方法");
}
@Override
public void b() {
System.out.println("重写B接口方法");
}
}
public static void main(String[] args) {
C c = new C();
c.a();
c.b();
}
**注意:一个类实现多个接口,多个接口不能有同名的方法,编译无法识别 **
实现类实现接口,重写一部分抽象方法,实现类还是一个抽象类
public interface A {
public abstract void a1();
public abstract void a2();
}
public abstract class B implements A {
public void a1(){
}
// public abstract void a2();
}
6.5 super
子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用 super 关键字,修饰 父类成员变量,类似于之前学过的 this 。
6.6 final
- fianl修饰类:该类不能被继承(不能有子类,但是可以有父类)
- final修饰方法:该方法不能被重写
- final修饰变量:表明该变量是一个常量,不能再次赋值
- 变量是基本类型,不能改变的是值
- 变量是引用类型,不能改变的是地址值,但地址里面的内容是可以改变的
7、多态
7.1 对象的多态性
同一个对象,在不同时刻表现出来的不同形态
多态的前提:
- 要有继承或实现关系
- 要有方法的重写
- 要有父类引用指向子类对象
父类 变量(对象名) = new 子类对象(); //多态写法
public class Person {
String s = "父类成员";
public void eat(){
System.out.println("人在吃饭");
}
}
public class Student extends Person {
String s = "子类成员";
public void eat(){
System.out.println("学生吃饭");
}
}
public static void main(String[] args) {
Person p = new Student();
//对象p,子类对象,调用成员变量s
System.out.println(p.s);
//子类对象调用方法
p.eat();//父类成员 学生吃饭
}
结论 : 成员方法编译看左边,运行看右边.成员变量都是左边
7.2 多态的转型
多态的程序中,不能调用子类的特有成员!!
只能调用子类父类的共有成员
class Animal {
public void eat(){
System.out.println("动物吃饭");
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void catchMouse() {
System.out.println("捉老鼠");
}
}
public class Test1Polymorphic {
/*
多态的前提:
1. 要有(继承 \ 实现)关系
2. 要有方法重写
3. 要有父类引用, 指向子类对象
*/
public static void main(String[] args) {
//创建对象,多态性
//父类 = new 任意子类对象() 扩展
Animal animal = new Cat();
animal.eat();
//Cat类的特有功能 catchMouse()方法
//类型转换,强制
//Cat提升为了Animal,转回Cat类型
Cat c = (Cat)animal;
c.catchMouse();
}
}
多态的好处和弊端
- 好处
提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作
- 弊端
不能使用子类的特有成员
多态中的转型
- 向上转型
- 父类引用指向子类对象就是向上转型
- 向下转向(强转)
- 子类型 对象名 = (子类型)父类引用;
7.3 多态中的转型异常
异常ClassCastException 类型转换异常,在多态中经常发生.
是在进行类型的强制转换的时候发生,我们现在的案例是Dog不能转成Cat.
需要解决这个异常 : 对象是Cat转Cat,是Dog换Dog
运算符 : 比较运算符,结果是boolean类型
运算符是关键字 instanceof
instanceof的语法格式:
对象名 instanceof 类的名字
解析: 比较这个对象,是不是由这个类产生的
c instanceof Cat 解释: c对象是不是Cat类产生的,如果是结果就是true
8、static 关键字
static修饰符 : 最早出现在main方法中.只能修饰成员,不能写在方法的内部,被static修饰的成员,静态成员变量和静态的成员方法
8.1 静态修饰成员变量
static 修饰的成员变量,是被所有的对象共享的数据.没有被static修饰的成员变量,是每个对象的独享数据或者是特有数据
static修饰的特点
- 被类的所有对象共享是我们判断是否使用静态关键字的条件
- 随着类的加载而加载,优先于对象存在
对象需要类被加载后,才能创建 - 可以通过类名调用
也可以通过对象名调用 - 静态成员跟随自己的类进入到元数据区(静态区域)
- 静态成员属于自己的类,不属于对象
- 静态成员进入内存后,赋默认值
- 静态成员变量的初始化实际早于对象
static关键字注意事项
- 静态方法只能访问静态的成员
- 非静态方法可以访问静态的成员,也可以访问非静态的成员
- 静态方法中是没有this关键字
8.2 静态代码块
写在类中方法外面 : static{}
静态代码块的执行时机 : 只要使用了这个类的成员(new对象,调用静态方法,静态变量),静态代码块就会执行,而且就一次
后面的课程会自己写静态代码块 : 数据库连接池 (C3P0,Druid)
JDBC注册数据库驱动程序,使用代码块
9. 内部类
所谓内部类,就是在一个类的内部,定义了另外的一个类
class A{ //外部类,封闭类
class B{} //内部类,嵌套类
}
9.1 成员内部类
成员内部类,是一个类定义在了另一个类的成员位置.这个内部类可以使用成员修饰符,public .static. final .private
对于内部来说 : 可以直接使用外部类的成员,如果外部类要使用内部类的成员,必须要创建对象.
语法: 外部类名.内部类名 = new 外部类对象().new 内部类对象();
//外部类
public class Outer {
public void outer(){
System.out.println("外部类的方法outer");
}
//内部类
public class Inner{
public void inner(){
System.out.println("内部类的方法inner");
}
}
}
public static void main(String[] args) {
//调用内部类的方法inner()
Outer.Inner oi = new Outer().new Inner();
oi.inner();
}
- 内部类Inner,编译后有class文件吗?有class文件,名字是外部类$内部类
- 内部类也是类,继承Object,可以实现接口
内部类是静态的调用方式 : 外部类名.内部类名 变量名 = new 外部类.内部类()
9.2 局部内部类
局部内部类 : 要定义在方法里面. 方法里面是局部位置,不能使用成员修饰符,权限,静态不能用
class A{
public void a(){
class B{} //局部内部类
}
}
public class Outer {
/**
* Inner类,是方法Outer的局部
* 依然方法,才能被外界访问
*/
public void outer(){
final int x=1;
class Inner{
public void inner(){
System.out.println("局部内部类的方法!!"+x);
}
}
//方法,建立对象
Inner inner = new Inner();
inner.inner();
}
}
public static void main(String[] args) {
//调用内部类的方法inner()
//直接调用,不能调用
Outer outer = new Outer();
outer.outer();
}
局部内部类,访问局部变量,变量必须final修饰
9.3 匿名内部类
匿名内部类,就是没有名字的内部类,只能写在方法中,为了简化代码书写.
- 匿名内部类使用的前提 :
- 必须有接口实现,或者是类的继承
- 格式 :
new 接口或者父类(){
//重写抽象方法
};
格式 == 实现类,实现接口,重写方法,创建对象
public interface MyInter {
public abstract void inter();
public abstract void inter2();
}
public class InnerClassTest {
public static void main(String[] args) {
//匿名内部类,简化书写,不写实现类
//同时调用多个重写方法
/*
* new MyInter(){}; 是接口实现类的匿名对象
* 多态 : 接口 变量 = 实现类对象
*/
MyInter my = new MyInter(){
@Override
public void inter() {
System.out.println("实现类实现接口重写方法");
}
@Override
public void inter2() {
System.out.println("实现类实现接口重写方法2222");
}
};
my.inter();
my.inter2();
}
}