类与对象:
对于一个项目我们首先看到的是一个个对象,对象是是真实存在的单个个体/东西,基于对象,我们抽出了类,类:即类别,代表一类个体。
所以可以这样说:把类比作是模板,对象就是模板中具体的实例。
类中可以包含:
1、对象的属性/特征----------成员变量
2、对象的行为/动作----------方法
一个类可以创建多个对象
//定义一个学生类(类是同一类别中多个对象的模板)
class Student{
//成员变量
String name;
int age;
String address;
//定义study()方法,无返回值,无参数
void study(){
System.out.println(name+"在学习");
}
//定义sayHi()方法,无返回值,无参数
void sayHi(){
System.out.println("大家好,我叫"+name+",今年”+age+"岁了,家住"+address);
}
}
public class StudentTest{
public static void main(String[] args){
Student zs = new Student();
//新建了一个Student类别的对象,对象名zs,它引用的是Student类别中的成员属性及方法
zs.name = "张三";//给对象zs的成员属性name赋值为“张三”
zs.age = 25;
zs.address = "北京";
zs.study();//调用对象方法
zs.sayHi();
//点击执行,输出结果为:
张三在学习
张三在学习,今年25了,家住北京
//若我们要创建多个对象,用此种方法(对象名.对象属性或方法名)会严重加重我们的代码量,重复代码多
//不是很贴合高质量代码(复用性高)的特点,所以引入了一种构造方法
补充:
类就是数据类型,比如cat类,对象就是一个具体的实例
从猫到对象的说法:(1)创建一个对象 (2)实例化一个对象 (3)把类实例化
这几个说法都是同一个意思
类和对象的区别和联系:类是抽象的,概念的,代表一类事物
对象是具体的,实际的,代表一个具体事物
类是对象的模板,对象是类的一个个体
关于为什么要引入对象的举例:
//单独变量来解决 =>不利于数据的管理(把一只猫的信息拆解了)
//第1只猫信息
String cat1Name = "小白";
int cat1age = 3;
String cat1Color = "白色";
//第2只猫信息
String cat2Name = "小花";
int cat2age = 10;
String cat2Color = "花色";
//数组 ===>(1)数据类型体现不出来
// (2)只能通过[下标]获取信息,造成变量名字和内容的对应关系不明确
// (3)不能体现猫的行为
//第1只猫信息
String[] cat1 = {"小白","3","白色"};
String[] cat2 = {"小花","10","花色"};
java内存对象的存在形式
构造方法(constructor):
构造方法也称构造函数、构造器、构建器-------复用给成员变量赋初值代码
1、作用:给成员变量赋初始值
2、与类同名,没有返回值类型(连void都没有)
3、在创建(new)对象时被自动调用
4、若自己不写构造方法,则编译器默认提供一个无参构造方法,若自己写了构造方法,则不再默认提供
5、构造方法可以重载
6、构造方法也属于类中成员,可以用4种访问修饰符来修饰
补充:
构造方法必须与类同名,普通方法也可以与类同名
public class Test {
Test(){//构造方法
System.out.println("构造方法");
}
void Test(){//无参普通方法
System.out.println("无参普通方法");
}
int Test(int a){//有参普通方法
return a;
}
public static void main(String[] args) {
Test t = new Test();
t.Test();
System.out.println(t.Test(5));
//System.out.println(t.Test());//编译错误,无参普通方法Test()中没有返回值,
//只有有返回值才能另外用println输出
}
}
/*
输出:构造方法
无参普通方法
5
*/
成员变量与局部变量:
成员变量:写在类中,方法外--------有默认值,可以在整个类中访问
局部变量:写在方法中(包括方法的参数)-------没有默认值,只能在当前方法中使用,在调用方法时,有参需要传参
成员变量能够在类中各个地方调用,而局部变量只能在声明的方法中使用
class Boo{
int a;
void show(int b){
int c;
System.out.println(a);
System.out.println(b);
System.out.println(c);//编译错误,c在使用之前必须先声明并初始化
}
}
Boo o = new Boo();//在new对象时,a已经有个默认值0
o.show(5);//----b=5,只要调用方法,有参传参
在创建(new)对象时,所有成员变量都会赋默认值
成员变量: 分为两种
1、实例变量:没有static修饰的,属于对象的,存储在堆中,有几个对象就会有几份实例变量,
通过引用(对象)点来访问
2、静态变量:由static来修饰,属于类的,存储在方法区中,只有一份,通过类名点来访问
成员变量与局部变量时可以重名的:
1、使用的时候默认采取就近原则;
2、当重名时,要区分所赋予的使用范围,用关键字this,this只能用在方法中;
关键字:this
this:指代当前对象,哪个对象调用方法它指的就是哪个对象。
this只能用在方法中,方法中访问成员变量之前默认有个this
public class Student{
//成员变量:类中定义的,在整个类中
String name;
age int;
String address;
//局部变量:方法中定义的,包括参数列表中的变量
Student(String name1,int age1,String address1){
name = name1;
age = age1;
address = address1;
}
}
//this的应用:
//刚刚在区分成员变量和局部变量时,Student构造方法中,定义了三个局部变量name1,
//age1,address1,然后再把这三个变量分别赋给三个局部变量,在实际中,这种赋值复用性不高
//那么,当局部变量名与成员变量名相同时,此时用下面一种写法:
public class Students{
String name;
age int;
String address;
Students(String name,int age,String address){
this.name = name;
this.age = age;
this.address = address;
}
//this的默认值:看不见,但系统有默认
void sayHi(){
System.out.println('我叫"+name+",今年"+age+"岁了,家住"+address);
//此时的三个局部变量name,age,address前面默认有this,但因为无局部变量(也无重名)所以不
//用this来特别区分这三个变量为成员变量
}
内存管理:
内存是由JVM(java虚拟机)来管理的(运行期发生),分为:堆、栈、方法区。
堆:
存储new出来的对象(该对象包括它的成员变量中的实例变量)
垃圾:没有任何引用所指向的对象就是垃圾
垃圾回收器(GC):不定时到内存中清扫垃圾,回收过程是透明的(看不到的);
并不一定发现垃圾就立刻回收,调用System.gc()可以建议JVM尽快调度
GC来回收
实例变量的生命周期:创建(new)对象时存储在堆中,对象被回收时一并被回收。
内存泄漏:不再使用的对象还没有被及时的回收,严重的会导致系统的奔溃。
潜艇游戏OutOfBounds()方法也是为了防止内存泄漏。
建议:当确定对象不再使用时,应及时将引用设置为null
实例一:
分析:new了一个Bomb对象,存储在堆中,局部变量b1、b2存储在栈中,同时有一个地址,
当 b2=b1时,会把b1的地址赋给b2
当b1=null时,b1的地址没了,指向对象的线也就没了,但b1没了,b2还在,地址也还在
若b2=null时,此时就不能访问Bomb对象
访问对象必须通过引用,当对象没有指引了,就会成为垃圾,而垃圾就会被GC收走
栈:
存储正在调用的方法中的局部变量(包括方法的参数)
调用方法时,会在栈中为该方法分配一块对应的栈帧,栈帧中存储局部变量(包括方法参数)
方法调用结束时,栈帧被自动清除,局部变量一并被清除。
方法没有被调用,方法栈帧一定是没有的
局部变量的生命周期:调用方法时存储在栈中,方法调用结束时与栈帧一并被清除。局部
变量清除,其指向的地址也清除,没有指向的对象会被GC回收。
局部变量中拼接的字面量(str = "hello";str1 = str + "!!!"),若str变量
是main方法中的,str1是普通方法中的变量,那么在普通方法调用完毕
之后,str1拼接的变量也会被GC清除调,而不会放到常量池中。
实例二:
分析:
当new了一个Aoo的对象时,Aoo对象包含实例变量a(默认值为0)存储到堆中,并会分配
一个main方法的栈帧,里面存储局部变量o和地址,在栈中
一开始栈中没有局部变量b和c,当o.show(8),即调用方法时,会分配show()的栈帧,一旦调完,show()的栈帧自动被清除,程序继续走,一旦main走完,main栈帧也会被清除,main栈帧清除了,指向也就没有了,Aoo对象成为垃圾被收走(main结束,全部清光)
方法区:
存储.class字节码文件(包括静态变量、所有方法)
方法只有一份,通过this来区分具体的调用对象
实例三:
分析:
当三块同时出现,先画方法区,方法区先生成.class字节码文件,然后是构造方法及普通方法等
然后在堆中会分配对象
第2个代码:方法区仍然不变,一个类只被加载一次,堆中会再new一个Bomb对象
当调用move()方法时,move也只有一份,方法区中是通过this来区分对象的
补充:
在java中,加载类的时候会自动执行静态块,当创建对象的时候会自动调用构造方法,当有继承类时,一定是先执行超类的而后再执行派生类的
class A{//超类
static{
System.out.print("A");
}
A(){
System.out.print("a");
}
}
class B extends A{//派生类
static{
System.out.print("B");
}
B(){
//第一行默认有超类的无参构造方法
System.out.print("b");
}
public static void main(String[] args) {
A a = new B();
}
}
/*
输出:ABab
*/
程序执行过程:
1.超类的static块 2.派生类的statec块 3.超类的构造方法 4.派生类的构造方法
内存图(堆、栈):
补充:地址是十六位进制
对象类型:是一种引用类型
栈 堆
内存分析:
1、new了一个Student类型的对象,Student对象及其成员变量存储在“堆”中 。该对象名zs因为在main方法中,属于局部变量,会放到“栈”中,因为对象是一种引用类型,zs会附带一个地址指向Student对象,当修改zs的名字为“李四”,zs会顺着地址1找到Student对象,再将name 中存储的“张三”修改为“李四”
2、同上,ww顺着地址2找到另一个Student对象,再将成员变量age修改为22
8种基本类型(以int类型为实例):
内存分析:
main方法中定义的变量属于局部变量,直接放到“栈”中,因为没有new值,所以堆中没有东西
基本类型数组(以int数组类型为例)
数组是一种引用类型,数组也是一种对象,所以存储在“堆”中,将数组的元素当做成员变量一并存储在“堆”中。
内存分析:
只要new了,就会放到“堆”中,在new的同时,int数组赋予默认值,三个元素i默认值为0,arr数组名在main方法中,局部变量放到“栈”中,并附带一个地址3指向int[]对象的第一个元素
引用类型数组(数组对象):
内存分析:
new了一个Student数组对象,放到“堆”中,默认值看成员变量类型,局部变量stus放到“栈”中,附带地址4指向Student数组对象的第一个元素,new了两个Student对象,数组是一个引用类型,数组stus[0]、stus[1]分别附带地址5、地址6指向各自的数组对象,当修改stus[1]的age时,会先后通过两个地址4和地址6找到Student对象中的age修改为22
空指针异常(NullPointerException)
演示:
public class Student {
private String name;
private int age;
private String address;
public Student(String name,int age,String address){
this.name = name;
this.age = age;
this.address =address;
}
private void sayHi(){
System.out.println("大家好,我叫:"+name+",今年:"+age+"岁,家住:"+address);
}
public static void main(String[] args) {
Student[] students = new Student[2];
students[0] = new Student("张三",19,"廊坊");
students[0].sayHi();
//大家好,我叫:张三,今年:19岁,家住:廊坊
students[0].name = null;//String类型默认值是null,所以也可以赋值null
students[0].sayHi();
//大家好,我叫:null,今年:19岁,家住:廊坊
students[0] = null;//当给一个引用名赋值null时,此时地址就会丢失,形成空指针
students[0].sayHi();//没有了地址,也就不能调用成员方法
//Exception in thread "main" java.lang.NullPointerException
}
}
一个对象的成员属性:若是String类型,它可以被赋值为null
但是对象的引用不可以赋值null,否则就丢失地址,形成空指针,无法访问成员属性及方法
引用类型数组与基本类型数组的区别:
引用类型数组(对象类型数组):1、给数组元素赋值必须new一下
2、若想访问对象数据必须通过数组元素打点
Student[] stus = new Student[3];
stus[0] = new Stedent("张三",27,"廊坊");
System.out.println(stus[0].age);
继承(extends):
通过extends来实现继承,作用:实现代码的复用性
两个类:
超类/父类:装共有的属性和行为
派生类/子类:装特有的属性和行为
特点:
1、派生类既能访问自己的,也能访问超类的,但是超类不能访问派生类的
2、一个超类可以有多个派生类,一个派生类只能有一个超类----单一继承
3、具有继承性:父传子,子子相传。
java规定:
1、构造派生类之前必须先构造超类。
2、在派生类的构造方法中若没有调用超类的构造方法,则默认super()调用超类的无参构造方法
若调用了超类的构造方法,则不再默认提供。
补充:
1、继承要符合is(是)的关系,不能为了复用代码就乱继承
2、继承意味着代码虽然我没有写,但也属于我,只是没有写在一起而已
3、继承的是超类中的成员变量和普通方法,而不包括构造方法
4、超类的构造方法时被派生类通过super来调用的
5、子类不能覆盖超类中的final或static方法
class Aoo{
int a;
Aoo(){}
void show(){}
}
class Boo extends Aoo{
//Boo继承的是Aoo中的成员变量a和普通方法show(),而Aoo中的无参构造方法只能通过Boo的构造方法
//使用关键字过super来调用
}
3、super()调用超类构造方法,必须位于派生类构造方法的第1行。
class Aoo{
Aoo(){
System.out.println("超类构造方法");
}
}
class Boo extends Aoo{
Boo(){
//默认第一行:super();
System.out.println("派生类构造方法");
}
}
class Test{
public static void main(String[] args){
Boo b = new Boo();
}
}
//输出结果为:
// 超类构造方法
// 派生类构造方法
// 程序从Test类main方法中开始执行,首先new了一个Boo类型的对象,同时调用Boo的无参构造方法,
//程序进入Boo无参构造方法中,因为java规定,派生类继承超类,派生类的构造方法中默认调用超类的
//无参构造方法,且位于派生类构造方法中的第1行,所以先输出“超类构造方法”,再输出“派生类构造方法”
class Coo{
Coo(int a){
}
}
class Doo extends Coo{
Doo(){
super(5);
}
}
//当超类定义为有参构造方法时,则不再默认提供super(),super(5)为调用超类有参构造
关键字:super
super:指代当前对象的超类对象
用法:
1、super.成员变量名------------访问超类的成员变量
2、super.方法名()-------------调用超类的方法
3、super()--------------------调用超类的无参构造方法
向上造型:
当多种角色干的事情是一样的,可将多个角色的对象造型到超类数组中,统一访问---实现方法复用
1、超类的引用指向派生类的对象
2、规定:能点出来什么,看引用的类型
new一个超类的对象,能调用超类的成员变量和超类的方法
new一个派生类的对象,既能调用自己的,也能调用超类的成员变量和方法
new一个超类类型,派生类的对象,此为向上造型,能点出来看引用的类型里面有什么(超类类型)
class Aoo{
int a;
void show{}
}
class Boo extends Aoo{
int b;
void test{}
}
class Test{
Aoo aoo = new Aoo();
aoo.a = 0;
aoo.show();
aoo.b = 1;//编译错误,超类不能继承派生类的成员变量
aoo.test();//编译错误,超类不能继承派生类的方法
Boo boo = new Boo();
boo.b = 0;
boo.test();
boo.a = 1;//派生类可以继承超类的成员变量和普通方法
boo.show();
Aoo boo1 = new Boo()
boo1.a = 0;
boo1.show();
boo1.b = 1;//编译错误,能点出来的只能是引用类型Aoo的成员变量和方法
boo1.test();//编译错误
}
若定义了一个超类类型,派生类的对象,因为是超类的类型,那么想访问派生类的方法该用什么办法?这时就用到了方法的重写-------
方法的重写:重新写
1、发生在父子类中,方法名相同,参数列表相同
------用在派生类中修改超类(方法)所声明的方法
2、规定:重写方法被调用时,看对象的类型
class Aoo{
void show(){
System.out.println("超类方法");
}
}
class Boo extends Aoo{
void show(){
System.out.println("方法重写");
}
}
class Test{
Aoo boo = new Boo();
boo.show();//重写了超类Aoo中show()方法,调用的是Boo中方法名相同,参数类型
//相同的show()
}
// 输出:方法重写
class Coo extends Aoo{
void show{
super.show();//若Coo想在自己方法基础上也想用超类方法,可在第一行加super.方法名()
System.out.println("方法重写");
}
}
class Test{
Aoo coo = new Coo();
coo.show();//可同时调出超类Aoo方法,也可调自己的方法
}
// 输出:超类方法
// 方法重写
规则:
遵循“两同两小一大”原则-------一般都是一模一样的
1、两同:方法名相同,参数列表相同
2、两小:1、派生类方法的返回值类型小于或等于超类方法的
void和基本类型时,必须相等
引用类型时,小于或等于
2、派生类抛出的异常小于或等于超类方法的
3、一大:派生类方法的访问权限大于或等于超类方法的
包名、修饰词
package:声明包
1、避免类的命名冲突
2、同包中的类不能同名,但不同包中的类可以同名
3、包名常常用层次结构(多数开发人员会采用大多域名倒写的方式),建议所有字母都小写,
Java的包命名不允许以数字开头,但可以以下划线_作为开头
4、包的声明应该在文件的第一行,而且每个Java文件只能有一个包声明
class:声明类
1、在命名主类名称时,须注意大小写,第一个字母须大写,同时主类名称要与文件名称保持一致。
2、类的全称:包名.类名
import:导入类
1、同包中的类可以直接访问
2、不同包中的类不能直接访问,若想访问:
(1)先import声明类,再访问类------建议
(2)写类的全称(包名.类名)--------不建议
3、类文件中可以引入任意数量import声明,import声明必须在package包声明后面,类声明之前
访问控制修饰符:保护数据安全
数据(成员变量)私有化(private),行为(方法)大多公开化(public)
1、public(公开的):本类、任何类
2、protected(受保护的):本类、派生类、同包类
3、默认的(没有修饰词default):本类、同包类
4、private(私有的):本类(私有的变量、方法仅可在本类中调用)
注:1、类、接口、抽象类的访问权限只能是public或默认的
2、类中的成员(包括构造方法)访问权限可以是这4种修饰符
3、访问权限由高到低:public>protected>默认的>private
4、私有变量及方法仅可在本类中调用
5、public权限,其他类文件(无论是否在该文件的包目录下)都可以直接进行调用,
调用时只需用import关键字引入即可
final
最终的、不可改变的-------单独应用的几率极低
1、修饰变量:变量不能被改变
2、修饰方法:方法不能被重写
3、修饰类:该类不能被继承(但该类可以继承别的类)
static:静态的
静态变量:
1、用static修饰
2、属于类,存储在方法区中,只有一份
3、常常通过类名点来访问
4、何时用:所有对象所共享的数据(图片、音频、视频等)
class Loo{
int a;
static int b;
Loo(){
a++;
b++;
}
void show(){
System.out.println("a="+a+",b="+b);
}
}
public class StaticDemo{
Loo o1 = new Loo();
o1.show();
Loo o2 = new Loo();
o2.show();
Loo o3 = new Loo();
o3.show();
System.out.println(Loo.b);
}
//输出:a=1,b=1
a=1,b=2
a=1,b=3
b=3
静态成员不会创建对象,只作为类的共享属性进行管理,所有使用了静态成员的地方都共享同一个静态成员 。
内存分析:只要进入了class,就生成.class字节码文件,存放到“方法区”
new了一个Loo对象,产生o1地址,此时实例变量a和静态变量b都为0,实例变量属于
对象的属性放到“堆中”,而静态变量b放到方法区,然后再调用Loo()构造方法,a+1
变为 1,b+1变为1,再通过(o1地址指向)对象名.方法访问show()方法,输出a和b;
然后又创建一个新的Loo对象,此时new了一个新的a,但是在后续执行过程中,a始终
在新的对象中+1,而b由0被多次修改,最后结果为3。
静态方法:
1、用static修饰
2、属于类,存储在方法区中,只有一份
3、常常通过类名点来访问
4、静态方法中没有this关键字,不能直接访问实例成员(可以访问静态成员)
5、何时用:方法的操作与对象无关
public class Aoo{
int a;//实例变量,属于对象,存放在堆中,对象点来访问
static int b;//静态变量,属于类,存放在方法区,类名点来访问
void show(){
System.out.println(a);//a为实例变量,方法中默认前面有this,
//即this.a指代当前对象的实例变量
System.out.println(b);//b为静态变量,方法中默认前面有类名Aoo
//即Aoo.b
}
static test(){
//static修饰的方法,变量名没有隐式this,没有this意味着没有对象,而实例变量需要
//对象名打点来访问
//System.out.println(a);//编译错误,没有了隐式this
System.out.println(b);//前面仍然默认有类名.b
}
}
public class Boo{
public static void main(String[] args){
Aoo.test();//静态的方法,不用new,可直接通过类名.方法名来访问
}
//输出:0
public class Coo{
int a;
void show(){//a为实例变量,属于对象,所以show方法不能设计为静态方法
System.out.println(a);
}
//plus方法中变量没有与对象有关的,可以用static来修饰
static int plus(int num1,int num2){
int num = num1 + num2;
return num;
}
}
静态块:
1、用static修饰
2、属于类,在类被加载期间自动执行,一个类只加载一次,所以静态块只执行一次
3、何时用:加载/初始化静态资源(图片、音频、视频等)
4、可以同时出现多个静态块,按照从上往下的顺序依次执行
class Aoo{
static{//静态块放到构造方法前后都一样
System.out.println("静态块");
}
Aoo{
System.out.println("构造方法");
}
}
public class Boo{
public static void main(String[] args){
Aoo aoo = new Aoo();
}
//输出:静态块
构造方法
public class Coo{
public static void main(String[] args){
Aoo aoo = new Aoo();
Aoo aoo1 = new Aoo();
}
//输出:静态块
构造方法
构造方法
//由此可得出:静态块在类被加载期间先被执行,且一个类只执行一次静态块
public class Test extends Thread {
static int x = 10;
static{
x += 5;
}
public static void main(String[] args) {
System.out.print("x="+x);
}
static{
x /= 3;
}
}
//在用到Test类的时候就会将Test.class以及静态变量x加载到方法区中
//同时会自动执行静态块,如果同时出现多个静态块,则按照从上往下的
//顺序依次执行
//初始x为10,增5变为15,后又除以3变为5
static final 常量
1、必须声明同时并初始化
2、常量不能被改变,通过类名点来访问
3、建议:常量名所有字母都大写,多个单词用_来分隔
4、编译器在编译时会将常量直接替换为具体的数,效率高
5、何时用:数据永远不变,并且经常使用
abstract 抽象
抽象方法:
1、由abstract修饰
2、只有方法的定义,没有具体的实现(连{}都没有)
public abstract class Aoo{
public abstract show();//定义了一个抽象的方法,也必定是抽象类
}
抽象类:
1、由abstract修饰
2、包含抽象方法的类必须是抽象类
3、抽象类不能被实例化(new对象)
4、抽象类是需要被继承的,派生类:
(1)重写所有抽象方法----变不完整为完整
(2)也声明为抽象类------一般不这么用
5、抽象类的意义:
(1)封装共有的属性和行为------实现代码复用
(2)为所有派生类提供统一 的类型------向上造型
(3)可以包含抽象方法,为派生类提供统一的入口(作用是能点出来)
派生类的行为不同,但入口是一致的,同时相当于定义了一个标准(强制重写)
成员内部类
局限了访问,应用率低。
1、类中套类,外面的称为外部类,里面的称为内部类
2、内部类通常只服务于外部类,对外不具有可见性
3、内部类对象通常在外部类中创建
4、内部类中可以直接访问外部类的成员(包括私有的)
内部类中有个隐式的引用指向了创建它的外部类对象:外部名.this
class Mama{
int a;
void creat(){
Baby b = new Baby();//内部类对象只能在外部类中创建
b.show();//调用内部类中show()方法
b1.show();//放到方法中调用正确
}
Baby b1 = new Baby();
//b1.show();//编译错误,内部类方法不能直接在类中调用
class Baby{//内部类
int b;
void show(){
System.out.println(a);//省略写法
System.out.println(Mama.this.a);//完整写法
//System.out.println(this.a);//编译错误,this指代当前对象,即Baby,Baby中无变量a
System.out.println(this.b);//正确
}
}
}
class InnerDemo{
public static void main(String[] args){
Mama m = new Mama();//创建一个Mama类型对象
//Baby b = new Baby();//编译错误,内部对象只能在外部类中创建,
//对外不具有可见性,不能在外部类外创建
匿名内部类
大大简化了代码
1、若想创建一个类(派生类)的对象,并且对象只被创建一次,可以做成匿名内部类
2、规定:在匿名内部类中默认外面的变量为final的
3、内部类中也有独立的.class文件
有名内部类:外部类名$内部类名.class
匿名内部类:外部类名$1.class(后面若再new,数字递增)
class Demo{//做匿名类
//(1)创建了Aoo的一个派生类,但是没有名字(匿名)
//(2)为该派生类创建了一个对象,对象名是aoo
//(3)大括号中的为派生类的类体
Aoo aoo = new Aoo(){//做一个Aoo的匿名类
void show(){
System.out.println("重写方法");
};//因为Aoo类中有抽象方法,所以show()方法必须重写
Boo boo = new Boo(){做一个Boo的匿名类
}//Boo类中没有抽象方法,不用重写
}
abstract class Aoo{
abstract void show();//有抽象方法
}
abstract class Boo{
}//无抽象方法
隐式的引用
1、this:指代当前对象
2、super:指代当前对象的超类对象
3、外部类.this:指代当前对象的外部类对象
面试题:
1、重写和重载的区别:
(1)重写(override/overring):发生在父子类中,方法名相同,参数列表相同
用在派生类中修改超类(方法)所声明的方法。
(2)重载(overload/overloading):发生在同一类中(也可以是派生类继承超类的),
方法名相同,参数列表不同。完全不同的方法,只是方法名正好相同。
2、实例对象与静态变量的区别:
(1)实例对象:属于对象的,在创建对象的时候存储在堆中。
创建多少个对象,则实例变量就会在堆中存在多少份
通过引用(对象)来访问
(2)静态变量:属于类的,在类被加载期间存储在方法区中,无论创建多少个对象,
静态变量 在方法区中都只存在一份
通过类名打点来访问
接口
1、是一种引用数据类型
2、由interface定义
3、只能包含常量和抽象方法
4、接口不能被实例化(new对象)
5、接口是需要被实现/继承的,实现类/派生类,必须重写所有抽象方法
6、一个类可以实现多个接口,用逗号分隔,若又继承又实现时,应先继承后实现
//接口的语法
interface Inter{
public static final int NUM = 5;//接口中成员的访问权限只能是public的
abstract void show();
int COUNT = 10;//接口中默认前面有public static final
void test();//接口中默认方法为抽象方法,默认public abstract
//int number;//编译错误,接口中默认为常量,必须声明同时初始化
//void say(){}//编译错误,接口中默认抽象方法不能有方法体
}
//演示接口继承接口
interface Inter1{
void show();
}
interface Inter2 extends Inter1{
void test();
}
//接口的实现
class Aoo implements Inter2{
public void show(){}//抽象方法必须被重写,且访问权限只能是public
public void test(){}
}
public class InterfaceDemo{
public static void main(String[] args){
//Inter2 inter = new Inter2();//编译错误,接口不能被实例化,
//想要实现,必须有实现类,向上造型
Inter2 aoo1 = new Aoo();//可以造型为它所实现的接口
Inter1 aoo2 = new Aoo();//向上造型
}
//接口多实现
interface Inter3{
void show();
}
interface Inter4{
void test();
}
abstract class Aoo{
abstract void say();
}
class Boo extends Aoo implements Inter3,Inter4{//先继承后实现
public void show(){}
public void test(){}
public void say(){}
}
类和接口的关系
1、类和类-------继承extends
2、接口和接口-------继承extends
3、类和接口-------实现implement
设计规则:
1、将所有派生类所共有的属性和行为,放到超类中--------抽共性
2、若对象的行为都一样,设计为普通方法
若对象的行为不一样,设计为抽象方法
3、将部分派生类所共有的属性和行为,放到接口中--------抽共性
接口是对继承的单根性的扩展-------实现多继承
接口的意义:
1、实现多继承
2、制定了一套标准、规则
多态
意义
1、同一类型的引用指向不同的对象时,有不同的实现------所有抽象方法都是多态的
-----行为的多态:cut()、getImage()、move()都是多态的
2、同一个对象被造型为不同的类型时,有不同的功能------所有对象都是多态的
------对象的多态:我、你、水....
向上造型/自动类型转换
1、超类型的引用指向派生类的对象
2、能点出来什么,看引用的类型
3、能造型成为的数据类型有:超类+所实现的接口
强制类型转化
成功的条件有两种如下:
1、引用所指向的对象,就是该类型
2、引用所指向的对象,实现了该接口或继承了该类
强转时若不符合如上条件,则发生ClassCastException类型转换异常
建议在强转之前通过instanceof先判断引用的对象是不是该类型
public class Aoo{}
interface Inter{}
public class Coo extends Aoo{}
public class Boo extends Aoo implements Inter{}
public class MultiTypeDemo{
public static void main(String[] args){
Aoo o = new Boo();//Aoo的引用o指向了对象Boo
Boo o1 = (Boo)o;//o引用指向的对象是Boo类型,符合条件1
Inter o2 = (Inter)o;//o引用指向的对象实现了Inter接口,符合条件2
//Coo o3 = (Coo)o;//编译错误,o引用指向的对象既不是该类型,也没有继承Coo或实现Coo的接口
//会发生ClassCastException类型转换异常
if(o instanceof Coo){
Coo o4 = (Coo)o;
}else{
System.out.println("o不能强转为Coo类型");
}
}
//输出:o不能强转为Coo类型
补充:
1、图片存储位置:图片类型有四种:Image、ImageIcon等
点项目------新建一个目录(Directory)-------命名后将图片全部(ctrl+A)复制点目录名
2、注释创建或消除快捷键: 多行(Ctrl+Shift+/) 单行(Ctrl+/)