一.方法的回顾与加深
1.1方法的定义
格式: 修饰符 返回类型 方法名(参数列表) 异常抛出类型{...} 异常抛出类型可不写
修饰符:public、static、abstract、final等等都是修饰符,一个方法可以有多个修饰符,如main方法。修饰符没有先后顺序。
返回值:方法执行完如果有要返回的数据,那么就要声明返回数据的类型,如果没有返回的数据,那么返回类型就必须写void。只有构造方法(构造器)不写任何返回类型也不写void。
例如
public static String sayHello(){
return "hello";
}
public void print(String m){
System.out.println(m);
}
return与break区别:
return:
(1)return 从当前的方法中退出,返回到该调用的方法的语句处,继续执行。
(2)return 返回一个值给调用该方法的语句,返回值的数据类型必须与方法的声明中的返回值的类型一致。
(3)return后面也可以不带参数,不带参数就是返回空,其实主要目的就是用于想中断函数执行,返回调用函数处。
break:
(1)break在循环体内,强行结束循环的执行,也就是结束整个循环过程,不在判断执行循环的条件是否成立,直接转向循环语句下面的语句。
(2)当break出现在循环体中的switch语句体内时,其作用只是跳出该switch语句体。
参数列表:可以多参数;参数需要声明类型;参数类型可以不同。
异常抛出:如果方法中的代码在执行过程中,可能会出现一些异常情况,那么就可以在方法上把这些异常声明并抛出,也可以同时声明抛出多个异常,使用逗号隔开即可。
1.2方法的调用
非静态方法:
没有使用static修饰符修饰的方法,就是非静态方法。调用这种方法的时候,是"一定"要使用对象的。因为非静态方法是属于对象的。(非静态属性也是一样的)。
示例:
public class Student{
public void say(){}
}
main:
Student s = new Student();
s.say();
静态方法:
使用static修饰符修饰的方法。就是静态方法。调用这种方法的时候,"可以"使用对象调用。也"可以"使用类来调用,但是推荐使用类进行调用,因为静态方法是属于类的。(静态属性也是一样的)
示例
public class Student{
public void say(){}
}
main:
Student.say();
类方法之间的调用:
同静态方法或同非静态方法之间可以相互调用,若a是静态方法,b是非静态方法,a方法中不能直接调用b方法,但是b方法中可以直接调用a方法,静态方法不能调用非静态方法,非静态方法可以调用静态方法。
1.3调用时的传参
调用方法进行传参时,分为值传递和引用传递两种。
如果参数的类型是基本数据类型,那么就是值传递。
如果参数的类型是引用数据类型,那么就是引用传递。
值传递是实参把自己变量本身存的简单数值赋值给形参。
引用传递是实参把自己变量本身存的对象内存地址值赋值给形参。
值传递和引用传递本质上是一回事,只不过传递的东西的意义不同而已。
传递示例
//值传递
public class Test{
public static void changeNum(int a){
a = 10;
}
public static void main(String[] args){
int a = 1;
System.out.println("before: a = "+a); //1 changeNum(a);
System.out.println("after: a = "+a); //1
}
}
//引用传递
public class Demo03 {
public static void changeName(Student s){
s.name = "tom";
}
public static void main(String[] args){
Student s = new Student();
System.out.println("before: name = "+s.name); //null changeName(s);
System.out.println("after: name = "+s.name); //tom
}
}
class Student{String name;}
1.4this
1.4.1.this在类中的作用
区别成员变量和局部变量:
public class Student{
private String name;
public void setName(String name){
//this.name表示类中的属性name
this.name = name;
}
}
1.4.2调用类中其他方法
public class Student{
private String name;
public void setName(String name){
this.name = name;
}
public void print(){
//表示调用当前类中的setName方法
this.setName("tom");
}
}
//默认情况下,setName("tom")和this.setName("tom")的效果是一样的
1.4.3调用类中的其他构造器
public class Student{
private String name;
public Student(){
//调用一个参数的构造器,并且参数的类型是String
this("tom");
}
public Student(String name){
this.name = name;
}
}
this的这种用法只能在构造器中使用。普通的方法是不能用的,并且这局调用的代码只能出现在构造器中的第一句。
this意义:this在类中表示当前类将来创建出的对象。
二.面向对象
2.1类与对象的相关概念
面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据。
从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象。
从代码运行角度考虑是先有类后有对象。类是对象的模板。
类:共性。对象:个性。类给对象的创建提供参考模板。在java中,没有类就没有对象,类是根据具体的功能需求,进行实际的分析,最终抽象出来的。
Student s = new Student("小明",20);
s.study();
对象s就是Student类的一个实例,能够使用的是具体实例,而不是类。
使用类类型、数组类型、接口类型声明出的变量,都可以指向对象,这种变量就是引用类型变量,简称引用。在程序中创建出对象后,直接使用并不方便,所以一般会用一个引用类型的变量去接收这个对象,这个就是所说的引用指向对象。
对象是通过引用来操作的。对应的引用类型:基本类型有 byte short int long float double char Boolean。
2.2创建与初始化对象:
使用new关键字创建对象。
Student s = new Student();
无赋值则自动化默认值。调用构造器,=代表把对象内存地址值赋值给变量。
2.3构造器
特点:必须和类的名字相同;必须没有返回类型,也不能写void。
作用: 使用new创建对象的时候必须使用类的构造器;初始化值,即构造器中的代码执行后,可以给对象中的属性初始化赋值。
构造器重载:除了无参构造器之外,使用有参构造器在创建对象时候可以给属性赋值。
构造器之间的调用:使用this关键字,在一个构造器中可以调用另一个构造器的代码。
默认构造器:无参构造器也被称为默认的构造器,隐藏。
2.4类的属性与方法
2.4.1属性
属性用于定义该类或该类对象包含的数据或者说静态属性。
属性作用范围是整个类体。
属性的默认初始化:(在定义成员变量时可以对其初始化,如果不对其初始化,Java使用默认的值对其初始化。数值:0,0.0 char:u0000, boolean:false, 所有引用类型:null)
属性格式:
[修饰符] 属性类型 属性名 = [默认值];
2.4.2方法
方法是类和对象动态行为特征的抽象。方法很类似于面向过程中的函数。面向过程中,函数是最基本单位,整个程序有一个个函数调用组成;面向对象中,整个程序的基本单位是类,方法是从属于类或对象的。
方法格式:
[修饰符] 方法返回值类型 方法名(形参列表) {
// ....
}
类中就是://静态的数据 //动态的行为。
三.封装、继承与多态
3.1封装
1.使用private 修饰需要封装的成员变量。
2.提供一个公开的方法设置或者访问私有的属性。设置 通过set方法,命名格式: set属性名(); 属性的首字母要大写访问;通过get方法,命名格式: get属性名(); 属性的首字母要大写。
示例:
public class Student{
private String name;
public void setName(String name){
this.name = name;
}
public String getName(){return this.name;}
}
public class Test{
public static void main(String[] args){
Student s = new Student();
s.setName("tom");
System.out.println(s.getName());
}
}
3.作用:
提高程序的安全性,保护数据。
隐藏代码的实现细节。
统一用户的调用接口。
提高系统的可维护性。
便于调用者调用。
良好的封装,便于修改内部代码,提高可维护性。
良好的封装,可进行数据完整性检测,保证数据的有效性。
3.2继承
继承的本质在于抽象。类是对对象的抽象,继承是对某一批类的抽象。
Java中类只有单继承,没有多继承, 接口可以多继承!
继承关系的俩个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。
public class student extends Person{
//...........
}
一个子类只能"直接"继承一个父类,就像是一个人只能有一个亲生父亲一个父类可以被多子类继承,就像一个父亲可以有多个孩子。父类中的属性和方法可以被子类继承。
Java中接口和接口之间,有可以继承,并且是多继承。
子类中继承了父类中的属性和方法后,在子类中能不能直接使用这些属性和方法,是和这些属性和方法原有的修饰符(public protected default private)相关的。(父类中的属性和方法使用public修饰,在子类中继承后"可以直接"使用;父类中的属性和方法使用private修饰,在子类中继承后"不可以直接"使用)。
父类中的构造器是不能被子类继承的,但是子类的构造器中,会隐式的调用父类中的无参构造器(默认使用super关键字)。
3.2.1Object
Object:Java中的每一个类都是"直接" 或者 "间接"的继承了Object类。
System.out.println(任何对象 instanceof Object);
//输出结果:true
//注:任何对象也包含数组对象
//例如:
//编译后,Person类会默认继承Object
public class Person{}
//Student是间接的继承了Object
public class Student extends Person{}
3.2.2Super
子类继承父类之后,在子类中可以使用this来表示访问或调用子类中的属性或方法,使用super就表示访问或调用父类中的属性和方法。
Super的使用:
1.访问父类中的属性
2.调用父类中的方法
3.调用父类中的构造器。子类构造器中会隐式的调用父类的无参构造器,调用父类中无参时如:super("xiaoming"):不管是显式还是隐式的父类的构造器,super语句一定要出现在子类构造器中第一行代码。所以this和super不可能同时使用它们调用构造器的功能。因为它们都要出现在第一行代码位置。
super的注意事项:
1.用super调用父类构造方法,必须是构造方法中的第一个语句。
2.super只能出现在子类的方法或者构造方法中。
3. super 和 this 不能够同时调用构造方法。(因为this也是在构造方法的第一个语句)
super和this的区别:
1.代表事物不一样。this代表所属方法的调用者对象。Super代表父类对象的引用空间。
2.使用前提不一样。this在非继承的条件下也可以使用。 super只能在继承的条件下才能使用。
3.调用构造方法。this调用本类的构造方法。 super调用的父类的构造方法。
3.2.3方法重写
方法重写只存在于子类和父类(包括直接父类和间接父类)之间。在同一个类中方法只能被重载,不能被重写。
静态方法不能重写。父类的静态方法不能被子类重写为非静态方法,编译出错;父类的非静态方法不能被子类重写为静态方法,编译出错;子类可以定义与父类的静态方法同名的静态方法(但是这个不是覆盖)。
私有方法不能被子类重写,子类继承父类后,是不能直接访问父类中的私有方法。
重写的语法:
1. 方法名必须相同。
2. 参数列表必须相同。
3. 访问控制修饰符可以被扩大,但是不能被缩小: public protected default private
4. 抛出异常类型的范围可以被缩小,但不能被扩大。(ClassNotFoundException ---> Exception)
5. 返回类型可以相同,也可以不同,如果不同的话,子类重写后的方法返回类型必须是父类方法返回类型的子类型
例如:父类方法的返回类型是Person,子类重写后的返回类可以是Person也可以是Person的子类型。
一般情况下,重写的方法会和父类中的方法的声明完全保持一致,只有方法的实现不同。即大括号中代码不一样。
3.3多态
3.3.1多态的相关概念
一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多,一个对象的实现类型虽然是确定的,但是这个对象所属的类型可能有很多种,可以是这对象实际类型的任意父类型。。
例如:
Student s1 = new Student();
Person s2 = new Student();
Object s3 = new Student();
一个父类引用可以指向它的任何一个子类对象。例如
Object o = new AnyClass();
Person p = null;
p = new Student();
p = new Teacher();
p = new Person();
多态中的方法调用代码如下:
public class Person{
public void run(){}
}
public class Student extends Person{
public void run(){
//重写run方法
}
}
//调用到的run方法,是Student中重写的run方法
main:
Person p = new Student();
p.run();
子类继承父类,调用a方法,如果a方法在子类中没有重写,那么就是调用的是子类继承父类的a方法,如果重写了,那么调用的就是重写之后的方法。
一个变量x,调用一个方法test,编译器是否能让其编译通过,主要是看声明变量x的类型中有没有定义test方法,如果有则编译通过,如果没有则编译报错。而不是看x所指向的对象中有没有test方法。
原理:编译看左边,运行不一定看右边。
Java中的方法调用,是运行时动态和对象绑定的,不到运行的时候,是不知道到底哪个方法被调用的。
3.3.2重写、重载、多态之间的关系
重载是编译时多态,重写是运行时多态。
调用重载的方法,在编译期间就要确定调用的方法是谁,如果不能确定则编译报错。调用重写的方法,在运行期间才能确定这个方法到底是哪个对象中的。这个取决于调用方法的引用,在运行期间所指向的对象是谁,这个引用指向哪个对象那么调用的就是哪个对象中的方法。(java中的方法调用,是运行时动态和对象绑定的)
3.3.3多态的注意事项以及存在条件
1.多态是方法的多态,属性没有多态性。
2.编写程序时,如果想调用运行时类型的方法,只能进行类型转换。不然通不过编译器的检查。但是如果两个没有关联的类进行强制转换,会报:ClassCastException。
3.多态的存在要有3个必要条件:要有继承,要有方法重写,父类引用指向子类对象。
以下三种类型的方法是没有办法表现出多态特性的(因为不能被重写):
1. static方法,因为被static修饰的方法是属于类的,而不是属于实例的。
2.final方法,因为被final修饰的方法无法被子类重写。
3.private方法和protected方法,前者是因为被private修饰的方法对子类不可见,后者是因为尽管被protected修饰的方法可以被子类见到,也可以被子类重写,但是它是无法被外部所引用的。
3.3.4方法绑定
执行调用方法时,系统根据相关信息,能够执行内存地址中代表该方法的代码。分为静态绑定和动态绑定。
静态绑定:在编译期完成,可以提高代码执行速度。
动态绑定:通过对象调用的方法,采用动态绑定机制。这虽然让我们编程灵活,但是降低了代码的执行速度。这也是JAVA比C/C++速度慢的主要因素之一。JAVA中除了final类、final方、static方法,所有方法都是JVM在运行期才进行动态绑定的。
多态:如果编译时类型和运行时类型不一致,就会造成多态。
3.3.5instanceof与类型转换
1.instanceof
System.out.println(x instanceof Y);
该代码能否编译通过,主要是看声明变量x的类型和Y是否存在子父类的关系。有"子父类关"系就编译通过,没有子父类关系就是编译报错。输出结果是true还是false,主要是看变量x所指向的对象实际类型是不是Y类型的"子类型"。这与之后学习到的接口类型和这个是有点区别的。
2.类型转换
X x = (X)o;运行是否报错,主要是变量o所指向的对象实现类型,是不是X类型的子类型,如果不是则运行就会报错。
1.父类引用可以指向子类对象,子类引用不能指向父类对象。
2.把子类对象直接赋给父类引用叫upcasting向上转型,向上转型不用强制转型。如Father father = new Son();
3.把指向子类对象的父类引用赋给子类引用叫向下转型(downcasting),要强制转型。如father就是一个指向子类对象的父类引用,把father赋给子类引用son 即Son son =(Son) father;其中father前面的(Son)必须添加,进行强制转换。
4.upcasting 会丢失子类特有的方法,但是子类overriding 父类的方法,子类方法有效。downcasting向下转型,强制转,丢失父类被子类所重写掉的方法。
5.向上转型的作用,减少重复代码,父类为参数,调有时用子类作为参数,就是利用了向上转型。这样使代码变得简洁。体现了Java的抽象编程思想。
3.4static、public、abstract
3.4.1.static
1.静态变量和非静态变量的区别:
静态变量属于类的,"可以"使用类名来访问,非静态变量是属于对象的,"必须"使用对象来访问。(在加载类的过程中为静态变量分配内存,实例变量在创建对象时分配内存,所以静态变量可以使用类名来直接访问,而不需要使用对象来访问。)
静态变量对于类而言在内存中只有一个,能被类的所有实例所共享。实例变量对于类的每个实例都有一份,它们之间互不影响。
2.静态方法和非静态方法之间的区别
静态方法"不可以"直接访问类中的非静态变量和非静态方法,但是"可以"直接访问类中的静态变量和静态方法。非静态方法"可以"直接访问类中的非静态变量和非静态方法,也"可以"直接访问类中的静态变量和静态方法。(加载顺序的问题导致静态方法和非静态方法不能直接相互访问。)
注意:this和super在类中属于非静态的变量.(静态方法中不能使用)
父类的静态方法可以被子类继承,但是不能被子类重写,父类的非静态方法不能被子类重写为静态方法 。
3.代码块和静态代码块
public class Person{
{//代码块(匿名代码块)}
static{//静态代码块}
}
匿名代码块和静态代码块的执行:
因为没有名字,在程序并不能主动调用这些代码块。
匿名代码块是在创建对象的时候自动执行的,并且在构造器执行之前。同时匿名代码块在每次创建对象的时候都会自动执行。
静态代码块是在类加载完成之后就自动执行,并且只执行一次.
注:每个类在第一次被使用的时候就会被加载,并且一般只会加载一次。
匿名代码块和静态代码块的作用:匿名代码块的作用是给对象的成员变量初始化赋值,但是因为构造器也能完成这项工作,所以匿名代码块使用的并不多。静态代码块的作用是给类中的静态成员变量初始化赋值。
在构造器中给静态变量赋值,并不能保证能赋值成功,因为构造器是在创建对象的时候才指向,但是静态变量可以不创建对象而直接使用类名来访问。
4.创建和初始化对象的过程
Student s = new Student();
若Student类之前没有进行类加载,则按一下顺序:
1.类加载,同时初始化类中静态的属性。
2.执行静态代码块。
3.分配内存空间,同时初始化非静态的属性(赋默认值,0/false/null)。
4.调用Student的父类构造器。
5.对Student中的属性进行显示赋值(如果有的话)。
6.执行匿名代码块。
7.执行构造器。
8.返回内存地址。
示例:
public class Person{
private String name = "zs";
public Person() {
System.out.println("Person构造器");
print();
}
public void print(){
System.out.println("Person print方法: name = "+name);
}
}
public class Student extends Person{
private String name = "tom";
{
System.out.println("Student匿名代码块");
}
static{
System.out.println("Student静态代码块"); }
public Student(){
System.out.println("Student构造器");
}
public void print(){
System.out.println("student print方法: name = "+name);
}
public static void main(String[] args) {new Student();}
}
//输出
Student静态代码块
Person构造器
student print方法: name = null
Student匿名代码块
Student构造器
Student s = new Student(); Student类之前已经进行了类加载
//顺序
1. 分配内存空间,同时初始化非静态的属性(赋默认值,0/false/null)
2. 调用Student的父类构造器
3. 对Student中的属性进行显示赋值(如果有的话)
4. 执行匿名代码块
5. 执行构造器
6. 返回内存地址
5.静态导入:导入这个类里的静态方法。好处是这种方法的好处就是可以简化一些操作。
3.4.2final
用final修饰的类不能被继承,没有子类。
用final修饰的方法可以被继承,但是不能被子类的重写。
用final修饰的变量表示常量,只能被赋一次值。
修饰成员变量-非静态成员变量:只有一次机会,可以给此变量a赋值的位置有1.声明时赋值;2.匿名代码块中赋值;3.构造器中赋值(类中出现的所有构造器都要写)。
修饰成员变量-静态成员变量:只有一次机会,可以给此变量a赋值的位置:1.声明的同时赋值;2.静态代码块中赋值。
修饰引用变量:不能修改引用指向的内存地址。示例如下
main:
final Student s = new Student();
//编译通过
s.setName("tom");
s.setName("zs");
//编译报错,不能修改引用s指向的内存地址
s = new Student();
3.4.3abstract
abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类。抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。
抽象类,不能使用new关键字来创建对象,它是用来让子类继承的。
抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
子类继承抽象类后,需要实现抽象类中没有实现的抽象方法,否则这个子类也要声明为抽象类。
抽象类是不能被实例化,抽象类的目的就是为实现多态中的共同点,抽象类的构造器会在子类实例化时调用,因此它也是用来实现多态中的共同点构造。
抽象类和抽象方法起到一个框架作用。很方便后期的调用和重写。抽象方法是为了程序的可扩展性。重写抽象方法时即可实现同名方法但又非同目的的要求。
3.5接口
3.5.1接口的概念与本质
普通类:只有具体的实现;抽象类:具体实现和规范(抽象方法) 都有。接口:只有规范。
接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全面地专业地实现了:规范和具体实现的分离。
抽象类还提供某些具体实现,接口不提供任何实现,接口中所有方法都是抽象方法。接口是完全面向规范的,规定了一批类具有的公共方法规范。
从接口的实现者角度看,接口定义了可以向外部提供的服务。
从接口的调用者角度看,接口定义了实现者能提供那些服务。
接口是两个模块之间通信的标准,通信的规范。如果能把你要设计的系统之间模块之间的接口定义好,就相当于完成了系统的设计大纲,剩下的就是添砖加瓦的具体实现了。
接口的本质:接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。如果你是......,则必须能......。
接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
OO的精髓,是对对象的抽象,最能体现这一点的就是接口。
3.5.2接口与抽象类的区别
抽象类也是类,除了可以写抽象方法以及不能直接new对象之外,其他的和普通类没有什么不一样的。接口是另一种类型,和类是有本质的区别的,所以不能用类的标准去衡量接口。
抽象类:声明类的关键字是class。抽象类是用来被继承的,java中的类是单继承。
类A继承了抽象类B,那么类A的对象就属于B类型了,可以使用多态。
一个父类的引用,可以指向这个父类的任意子类对象。
继承的关键字是extends。
接口:声明接口的关键字是interface。接口是用来被类实现的,java中的接口可以被多实现。
类A实现接口B、C、D、E..,那么类A的对象就属于B、C、D、E等类型了,可以使用多态
一个接口的引用,可以指向这个接口的任意实现类对象。
实现的关键字是implements。
接口中可以不写任何方法,但如果写方法了,该方法必须是抽象方法。
3.5.3接口中的变量
接口中可以不写任何属性。但如果写属性了,该属性必须是public static final修饰的静态常量。可以直接使用接口名访问其属性。(因为是public static修饰的。)声明的同时就必须赋值.(因为接口中不能编写静态代码块)
3.5.4类与接口
一个类可以实现多个接口
public class Student implements A,B,C,D{
//Student需要实现接口A B C D中所有的抽象方法
//否则Student类就要声明为抽象类,因为有抽象方法没实现
}
A s1 = new Student();
B s2 = new Student();
C s3 = new Student();
D s4 = new Student();
s1只能调用接口A中声明的方法以及Object中的方法 ;s2只能调用接口B中声明的方法以及Object中的方法;s3只能调用接口C中声明的方法以及Object中的方法;s4只能调用接口D中声明的方法以及Object中的方法。必要时可以类型强制转换
一个接口可以继承多个父接口
public interface A{ public void testA();}
public interface B{ public void testB();}
public interface C extends A,B{public void testC();}
//Student相当于实现了A B C三个接口,需要实现所有的抽象方法
//Student的对象也就同时属于A类型 B类型 C类型
public class Student implements C{
public viod testA(){}
public viod testB(){}
public viod testC(){}
}
main:
C o = new Student();
System.out.println(o instanceof A);//true
System.out.println(o instanceof B);//true
System.out.println(o instanceof C);//true
System.out.println(o instanceof Student);//true
System.out.println(o instanceof Object);//true
System.out.println(o instanceof Teacher);//false
//编译报错
System.out.println(o instanceof String);
System.out.println(o instanceof X);
如果o是一个接口类型声明的变量,那么只要X不是一个final修饰的类,该代码就能通过编译
至于其结果是不是true,就要看变量o指向的对象的实际类型,是不是X的子类或者实现类了。
一个引用所指向的对象,是有可能实现任何一个接口的。(Java中的多实现)
3.5.5接口的作用
接口的最主要的作用是达到统一访问,就是在创建对象的时候用接口创建。
【接口名】 【对象名】= new 【实现接口的类】 用哪个类的对象就可以new哪个对象了,不需要改原来的代码。
假如两个类中都有个function()的方法,如果用接口,那new a();就是用a的方法,new b();就是用b的方法。这个就叫统一访问,因为实现这个接口的类的方法名相同,但是实现内容不同。
3.5.6总结
1.Java接口中的成员变量默认都是public,static,final类型的(都可省略)。必须被显示初始化,即接口中的成员变量为常量(大写,单词之间用"_"分隔) 2.Java接口中的方法默认都是public,abstract类型的(都可省略),没有方法体,不能被实例化 3.Java接口中只能包含public,static,final类型的成员变量和public,abstract类型的成员方法。 4.接口中没有构造方法,不能被实例化。
5.一个接口不能实现(implements)另一个接口,但它可以继承多个其它的接口
6.Java接口必须通过类来实现它的抽象方法。
7.当类实现了某个Java接口时,它必须实现接口中的所有抽象方法,否则这个类必须声明为抽象类
8.不允许创建接口的实例(实例化),但允许定义接口类型的引用变量,该引用变量引用实现了这个接口的类的实例。 9. 一个类只能继承一个直接的父类,但可以实现多个接口,间接的实现了多继承。
四.内部类
4.1概述
内部类就是在一个类的内部在定义一个类,比如A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了。
内部类分为四种:成员内部类、静态内部类、局部内部类、匿名内部类。
4.2成员内部类
成员内部类中不能写静态属性和方法。
定义内部类与实例化内部类:
public class Outer {
private int id=10;
public void out(){
System.out.println("外部类");
}
public class Inner{
public void in(){
System.out.println("内部类");
}
}
}
public class Test{
public static void main(String[] args) {
//实例化成员内部类分两步
//1、实例化外部类
Outer outObject = new Outer();
//2、通过外部类调用内部类
Outer.Inner inObject = outObject.new Inner();
//测试,调用内部类中的方法
inObject.in();//打印:这是内部类方法
}
}
内部类可以访问外部类的所有属性(这里的属性包括私有的成员变量,方法)。
如果内部类中的变量名和外部类的成员变量名一样,要通过创建外部类对象 "."属性来访问外部类属性,通过this.属性访问内部类成员属性。
内部类通用用法:
1.要想访问内部类中的内容,必须通过外部类对象来实例化内部类。
2.能够访问外部类所有的属性和方法,原理就是在通过外部类对象实例化内部类对象时,外部类对象把自己的引用传进了内部类,使内部类可以用通过Outer.this去调用外部类的属性和方法,
一般都是隐式调用了,但是当内部类中有属性或者方法名和外部类中的属性或方法名相同的时候,就需要通过显式调用Outer.this了。
4.3静态内部类
static一般只修饰变量和方法,平常不可以修饰类,但是内部类却可以被static修饰。
1.static修饰成员变量:整个类的实例共享静态变量。
2.static修饰方法:静态方法,只能够访问用static修饰的属性或方法,而非静态方法可以访问static修饰的方法或属性。
3.被static修饰了的成员变量和方法能直接被类名调用。
4.static不能修饰局部变量,切记,不要搞混淆了,static平常就用来修饰成员变量和方法。
静态内部类能够直接被外部类给实例化,不需要使用外部类对象。
Outer.Inner inner = new Outer.Inner();
内部类能够调用外部类的方法和属性,在静态内部类中就不行了,因为静态内部类没有了指向外部类对象的引用。除非外部类中的方法或者属性也是静态的。这就回归到了static关键字的用法。静态内部类中可以声明静态方法和静态变量,但是非静态内部类中就不可以声明静态方法和静态变量。
4.4局部内部类
局部内部类是在一个方法内部声明的一个类
局部内部类中可以访问外部类的成员变量及方法
局部内部类中如果要访问该内部类所在方法中的局部变量,那么这个局部变量就必须是final修饰的。
public class Outer {
private int id;
//在method01方法中有一个Inner内部类,这个内部类就称为局部内部类
public void method01(){class Inner{
public void in(){
System.out.println("这是局部内部类");
}}}}
在局部内部类中,如果要访问局部变量,那么该局部变量要用final修饰。
局部内部类不能通过外部类对象直接实例化,而是在方法中实例化出自己来,然后通过内部类对象调用自己类中的方法。局部内部类只能在自己的方法中用。
4.5匿名内部类
匿名对象:如果一个对象只要使用一次,那么我们就是需要new Object().method()。 就可以了,而不需要给这个实例保存到该类型变量中去。这就是匿名对象。
public class Test {
public static void main(String[] args) {
//讲new出来的Apple实例赋给apple变量保存起来,但是我们只需要用一次,就可以这样写
Apple apple = new Apple();
apple.eat();
//这种就叫做匿名对象的使用,不把实例保存到变量中。
new Apple().eat();
}
}
class Apple{
public void eat(){
System.out.println("我要被吃了");
}
}
匿名内部类跟匿名对象是一个道理:
匿名对象:只需要用一次,那么就不用声明一个该类型变量来保存对象了,
匿名内部类:也只需要用一次,那就不需要在类中先定义一个内部类,而是等待需要用的时候,就在临时实现这个内部类,因为用次数少,可能就这一次,那么这样写内部类,更方便。不然先写出一个内部类的全部实现来,然后就调用它一次,岂不是用完之后就一直将其放在那,那就没必要那样。
匿名内部类需要依托于其他类或者接口来创建。如果依托的是类,那么创建出来的匿名内部类就默认是这个类的子类如果依托的是接口,那么创建出来的匿名内部类就默认是这个接口的实现类。
匿名内部类的声明必须是在使用new关键字的时候。匿名内部类的声明及创建对象必须一气呵成,并且之后能反复使用,因为没有名字。
main:
A a = new A(){
//实现A中的抽象方法
//或者重写A中的普通方法
};
//大括号里面其实就是这个内部类的代码,只不过是声明该内部类的同时就是要new创建了其对象,
并且不能反复使用,因为没有名字。
B是一个接口,依托于B接口创建一个匿名内部类对象
B b = new B(){
//实现B中的抽象方法
};
1. 匿名内部类除了依托的类或接口之外,不能指定继承或者实现其他类或接口,同时也不能被其他类所继承,因为没有名字。
2. 匿名内部中,我们不能写出其构造器,因为没有名字。
3.匿名内部中,除了重写上面的方法外,一般不会再写其他独有的方法,因为从外部不能直接调用到。(间接是调用到的)。
五.内容来源
Java课程与笔记来源:
【狂神说Java】Java零基础学习视频通俗易懂_哔哩哔哩_bilibili
www.kuangstudy.com