Java-面向对象基本使用汇总
上篇说了面向对象的核心几个概念,但是没说使用,我们具体介绍一下:先介绍一下相关使用,再接着上篇讲解使用,本篇内容稍微有点多!更多的像是一个汇总。
类和对象
类和对象的概念之前说过,接下来讲解一下相关的使用,先看一下代码,用代码讲解使用比较直接
方法
方法(method)就是一段用来完成特定功能的代码片段
方法用于定义该类或该类的实例的行为特征和功能实现。 方法是类和对象行为特征的抽象。方法很类似于面向过程中的函数。面向过程中,函数是最基本单位,整个程序由一个个函数调用组成。面向对象中,整个程序的基本单位是类,方法是从属于类和对象的。
//手机类
public class MobilePhone {
private String name;
private int price;
/*方法声明格式:[修饰符] 返回值类型 方法名(形式参数列表){
Java语句;… … …
}
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public void show(){
System.out.printf("%s的价格为:%d",name,price);
}
}
/**
* 详细说明:
* 形式参数:在方法声明时用于接收外界传入的数据。形参可以为基本类型和引用类型,定义形参的时候跟定义变量方式一样
* 实参:调用方法时实际传给方法的数据。
* 返回值:方法在执行完毕后返还给调用它的环境的数据。
* 返回值类型:事先约定的返回值的数据类型,如无返回值,必须显示指定为为void。
* 返回值类型可以为基本类型和引用类型
* return语句要和返回值类型匹配上
*/
//手机导购类,只是测试一下代码
public class SalesPerson {
public static void main(String[] args) {
//MobilePhone是一个类不能直接使用,使用的时候要先创建对象,使用的关键字就是new
//创建对象的格式:类名 变量名=new 类名();
MobilePhone phone=new MobilePhone();
phone.setName("华为P40");
//方法调用:对象名.方法名(实参列表)
phone.setPrice(4499);
phone.show();
}
}
方法的作用:提高代码的复用性
可以将实现的细节封装起来,然后供其他用户来调用即可。
重载
重载上篇文章提过一点,就是方法有同样的名称,但是参数列表不同,这样的同名不同参数的方法之间,互相称之为重载方法
public class Test {
//同一个类中,方法名相同,参数列表不同(个数,顺序,类型),与修饰符和返回值类型无关
public int add(int num1, int num2) {
return num1 + num2;
}
public int add(int num1, int num2, int num3) {
return num1 + num2;
}
public double add(double num1, double num2) {
return num1 + num2;
}
/* 不构成重载
public static int add(int a,int b){
return a + b;
}
*/
}
这是String源码的示意图,方法名字都相同,但是参数列表不同
构造方法(构造器)
上面那个代码new之后的类名()其实是有含义的,它是一个无参构造方法,即:new调用了无参构造方法。你会好奇也没写无参构造方法啊,它怎么调用的?实际上系统会默认提供无参构造方法。那么有无参构造是不是有有参构造,还真有。
//手机类
public class MobilePhone {
private String name;
private int price;
//无参构造,默认系统提供的就是这个
public MobilePhone() {
}
//有参构造
public MobilePhone(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public void show(){
System.out.printf("%s的价格为:%d",name,price);
}
}
构造方法的格式:[访问修饰符] 构造方法名(){}
构造方法的特点:方法名和类名完全一致
没有返回值
参数列表可有可无,就看你的需要
如果有了有参构造,系统就不会提供无参构造,建议构造方法都匹配写上
构造方法可以重载,就像上面方法名字相同,而参数列表不同
构造方法的作用:给对象赋初始值进行初始化。无参构造是创建对象没进行初始化,后期再进行赋值,就是上面测试类创建对象之后调用set方法赋值;有参构造就是创建对象的时候并赋值
//手机导购类,只是测试一下代码
public class SalesPerson {
public static void main(String[] args) {
MobilePhone phone=new MobilePhone("华为P40",4699);
phone.show();
}
}
/*
就相当于手机出厂之后先有了一个定价,但是后续随着时间价格也进行了改变,对应到程序中就是调用phone.setPrice()方法改变值
*/
创建对象的过程:
第一次遇到MobilePhone的时候,进行类的加载(只加载一次)
创建对象,为这个对象在堆中开辟空间
为对象进行属性的初始化动作,属性赋值都是默认值
new关键字调用构造器,执行构造方法,在构造器中对属性重新进行赋值
局部变量和属性
写一段代码但是这个没有意义,只是为了测试知识点
public class TestVariable {
int num=10;//属性
String str;
public void test(){
int num=20;//局部变量
System.out.println("局部变量 num:"+num);
}
public static void main(String[] args) {
TestVariable var=new TestVariable();
System.out.println("属性 num:"+var.num);
var.test();
}
}
/*
执行结果:
属性 num:10
局部变量 num:20
*/
很直接的能看出属性和局部变量的一个区别,就是属性也叫做成员变量定义在类体中,而局部变量定义在方法中
如果两个num变量重名了,访问的时候遵循就近原则,方法里面访问num变量就是访问方法里面的,不能越过方法,而访问类的num变量就拿对象访问。
局部变量和属性区别:
代码中位置不同:成员变量:类中方法外定义的变量 ;局部变量:方法中和代码块中定义的变量
代码的作用范围:成员变量:当前类的很多方法;局部变量:当前一个方法(当前代码块)
关于默认值:成员变量:有默认值;局部变量:没有默认值
关于初始化:成员变量:不需要,不建议初始化,后续使用的时候再赋值即可;局部变量:一定需要,不然直接使用的时候会报错
内存中位置不同:成员变量:堆内存;局部变量:栈内存
作用时间不同:成员变量:当前对象从创建到销毁;局部变量:当前方法从开始执行到执行完毕
关键字:this
上面的那个MobilePhone应该还记着,里面有一个有参构造,然后上面还写了局部变量和属性的区别,那为什么对象创建之后调用构造方法赋值给了属性,重点就是那个this关键字
this的作用:指当前对象
比如:this.name就是调用构造方法之后,给构造方法赋的值再给属性name,如果没有this关键字,构造方法就要变成这样
public MobilePhone(String a, int b) {
name = a;
price = b;
}
程序起名字一般要见名知意,提高可读性;而代码这么写容易混淆这个构造方法给什么赋值。这里的this就是用来区分属性和局部变量。
再举个例子,如果让上面局部变量案例的test方法调用,打印结果为属性的值10的话,代码就是这样
public class TestVariable {
int num = 10;
String str;
public void test() {
int num = 20;
System.out.println("局部变量 num:" + this.num);
}
public void show(){
test();// test() == this.test(),this可以同类中方法调用,并且可以省略,格式:this.方法名() == test()
}
public static void main(String[] args) {
TestVariable var = new TestVariable();
System.out.println("属性 num:" + var.num);
var.test();
System.out.println("--------------------------");
var.show();
}
}
/*
执行结果:
属性 num:10
局部变量 num:10
--------------------------
局部变量 num:10
*
this还可以调用构造器
public class TestVariable {
int num;
String str;
public TestVariable(int num, String str) {
this(str);//构造器也可以互相调用,但this调用的构造器必须在第一行,格式:this(参数列表)
//为什么属性和方法都直接.而构造器独特,那是因为构造器格式: 类名(),this指代本类对象自然就也等同于类名
this.num = num;
}
public TestVariable(String str) {
this.str = str;
}
关键字:super
super关键字其实在上篇文章讲继承的体育比赛案例用到了
//体育比赛,会有很多项目,先只写一个大概的方法,可以理解成占位作用
public class SportsCompetition {
private String name;
public void playSports(String name){
System.out.printf("进行%s项目比赛!",name);
}
}
//田径比赛,重写父类的playSports方法,等待实际使用的时候传此方法的实参
public class TrackFieldCompetition extends SportsCompetition{
@Override
public void playSports(String name) {
//此方法表示调用父类的方法,但传入的是子类自己的参数
super.playSports(name);
}
}
//球类比赛,同样重写父类的playSports方法,等待实际使用的时候传此方法的实参
public class BallGame extends SportsCompetition {
@Override
public void playSports(String name) {
super.playSports(name);
}
}
super与this不同的地方就一个,那就是this指代的是当前对象而super指代父类对象,应用点都差不多
单独说一下super修饰构造器,子类的构造器一般第一行会默认提供一个super()来调用父类的构造,如果你显式提供了除外
注意:super与this不能同时使用
关键字:static
回过头看一下运行的主方法
/**
* public 访问修饰符,前面介绍它是公开访问的,所有类都能访问
* void 表示没有返回值
* main 主方法名
* () 里面写的是参数列表
* String[] 表示存储的是字符串类型的数组
* @param args 参数名
* 那么static表示的其实是静态,意思是数据可共享
*/
public static void main(String[] args) {}
我们介绍一下static关键字,static可以修饰:属性、方法、内部类、代码块
举个例子
public class Student {
String name;
//学生交的饭费,这是共享数据,也不用创建对象访问,学生教的饭费都一样,就用static修饰
//static修饰的变量是类变量,使用的时候可以直接使用类名访问,即:Student.
static int mealCost;
}
类变量的应用场景:某些特定的数据想要在内存中共享一处时,便可用static修饰变量
类变量的注意事项:
在类被加载的时候一起加载入方法区中的静态域中,然后随着类消亡而被销毁
先于对象存在
静态属性==静态变量=类变量,非静态变量=实例变量=普通变量
静态变量被所有的对象共享,属于公共变量;而实例变量对象是独享的
访问方式: 对象名.属性名 类名.属性名(推荐)
//这是工具类Collections类中的一个方法,用static方法修饰叫做类方法,同样是可以类名直接调用,Collections.swap()
private static void swap(Object[] arr, int i, int j) {
Object tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
类方法的应用场景:一般工具类中的方法定义为static,操作static属性的方法
类方法的注意事项:
静态方法中不能定义实例变量
不能直接访问实例方法和变量
静态方法中不能使用this关键字
静态方法可以被普通方法中调用,反之不可以
静态方法不能被重写
调用方式: 对象名.方法名 类名.方法名(推荐)
修饰的代码块和内部类后面会涉及
关键字:package
此关键字的作用就是打包
包的作用:
区分同名的类,比如一个学校有好多赵子龙,就把他们分到各自的班级
当类很多时,可以很好的管理类【模块】。类有很多,没有包的话显示查找的时候就很杂乱。
控制访问范围
常见包:
java.lang.* :lang包是基本包,默认引入,不需要再引入.
java.util.* :util 包,系统提供的工具包, 工具类,使用 Scanner
java.net.* :网络包,网络开发=> 网络开发
java.awt.* :是做java的界面开发,GUI
打包的例子:
//包声明的位置一般都在非注释性代码的第一行
package com.ty.keyword.packagedemo;
本质就是创建一个文件夹,以此保存很多类
关键字:import
此关键字的作用就是导包,引用其它的包来使用其下的类
//定义在package下方,类的上方
//只是导入ArrayList这一个类
import java.util.ArrayList;
//这个*指java.util包下所有的类和子包
import java.util.*;
//静态导包
import static java.lang.Math.*;
public class StaticImport {
public static void main(String[] args) {
System.out.println(random());
System.out.println(PI);
System.out.println(round());
}
//在静态导入后,同一个类中有相同的方法的时候,会优先走自己定义的方法
public static int round(){
return 1000;
}
}
//正常导包
import java.lang.Math.*;
public class StaticImport {
public static void main(String[] args) {
System.out.println(Math.random());
System.out.println(Math.PI);
System.out.println(round());
}
//同一个类中有相同的方法的时候,会优先走自己定义的方法
public static int round(){
return 1000;
}
}
关键字:final
翻译过来为最终/最后/终极,很见名知意,意思就是不可以被改变了
可以修饰变量、方法、类
修饰变量就表示这是一个常量,不能被修改了
public class FinalDemo {
public static void main(String[] args) {
final A demo = new A();
//demo= new A(); 引用类型,地址值不可变
final A a=new A();
a(a);
b(a);
}
public static void test() {
final int a = 20;
//a=50; 基本类型,地址值不可变
}
public static void a(A a) {
a = new A();
}
public static void b(final A a) {
//a = new A(); a被final修饰,表示引用不可变
}
}
修饰方法,就表示此方法不可被重写
public class Person {
public final void eat(){}
}
class Student extends Person{
@Override
public void eat() { //报错
System.out.println();
}
}
修饰类,表示该类不可被继承
public final class Person {
public final void eat(){}
}
class Student extends Person{} //报错
//String字符串类就被final修饰
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
}
代码块
代码块又称作初始化快,属于类的一部分(属性、方法、构造器、代码块、内部类 ),跟方法类似,将逻辑代码封装在里面,但是和方法有区别,没有方法名、参数列表、返回值类型,就单独的一对大括号{},用来初始化类和对象的信息,类加载或创建对象时自动就会调用。
代码块分类:普通块,构造块,静态块,同步块(多线程)
public class TestCodeBlock {
public TestCodeBlock() {
System.out.println("构造方法!");
}
//一对大括号{}括起来的代码叫做代码块
//定义在类中的代码叫做构造代码块
//每次代码运行的时候回将构造代码块中的代码添加到构造方法的前面,除了使用this(参数)的时候不会添加
{
System.out.println("构造代码块!");
}
//static修饰的代码块叫做静态代码块,程序加载时优先执行且只执行一次
// 一般创建工厂,数据库的初始化信息都放入静态块。用于执行一些全局性的初始化操作。
static {
System.out.println("静态代码块!");
}
public static void test() {
System.out.println("静态方法!");
}
public void show() {
// 定义在方法中的代码叫做普通代码块
{
System.out.println("普通代码块!");
}
//synchronized修饰的代码块叫做同步代码块,用来给共享空间进行加锁操作
synchronized (TestCodeBlock.class) {
System.out.println("同步代码块!");
}
}
上面代码关于构造代码块和构造编译之后顺序
public class Test {
public static void main(String[] args) {
TestCodeBlock.test();
TestCodeBlock block = new TestCodeBlock();
block.show();
}
}
/*
执行结果:
静态代码块!
静态方法!
构造代码块!
构造方法!
普通代码块!
同步代码块!
*/
public class TestExecutionOrder {
public static void main(String[] args) {
Son son = new Son();
}
}
class Father {
{
System.out.println("父类构造代码块!");
}
static {
System.out.println("父类静态代码块!");
}
public Father() {
System.out.println("父类构造方法!");
}
}
class Son extends Father{
{
System.out.println("子类构造代码块!");
}
static {
System.out.println("子类静态代码块!");
}
public Son() {
System.out.println("子类构造方法!");
}
}
/*
执行结果:
父类静态代码块!
子类静态代码块!
父类构造代码块!
父类构造方法!
子类构造代码块!
子类构造方法!
*/
面向对象的三大特性----封装、继承、多态的基本概念上篇文章介绍了,剩下的就是介绍一下使用和注意点
封装
封装上篇文章介绍得基本就差不多。即:保护类的内部细节,只对外提供访问方法,从而保护类的安全性;隔离复杂度
public class Girl {
private int age;
public Girl() {
}
public Girl(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age > 30) {
this.age = 18;
} else {
this.age = age;
}
}
}
class Test {
public static void main(String[] args) {
Girl girl = new Girl();
girl.setAge(25);
girl.setAge(32);
System.out.println(girl.getAge());
}
}
总结:
广义的封装:将某段代码提取到一个方法中,抽象出工具类,供外部进行调用
狭义的封装:将属性设置成私有,不能被外部访问
提供共有的getter/setter方法,供外部进行操作
可以实现复杂的内部判断逻辑
继承
java中继承用extends修饰,上篇文章也写过示例,这里只介绍几个在java中的特性
- Object类是所有类的根基父类,所有类的父类都继承自Object
/**
* Class {@code Object} is the root of the class hierarchy.
* Every class has {@code Object} as a superclass. All objects,
* including arrays, implement the methods of this class.
*
* @author unascribed
* @see java.lang.Class
* @since JDK1.0
*/
public class Object {}
- java中只有单继承,即:子类只有对应的一个父类。但一个父类可以有多个子类。
- 父类的私有信息,子类也继承过来,只不过因为权限修饰符的限制不能直接访问,需要父类提供一个公有方法或者通过反射方式访问
- 创建子类对象时先会创建父类对象
权限修饰符
同一个类 | 同一个包 | 子类 | 所有类 | |
---|---|---|---|---|
private | √ | × | × | × |
default | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
private:在当前类中可以访问
default:缺省修饰符:到同一个包下的其他类都可以访问
protected:最大到不同包下的子类
public:在整个项目中都可以访问
重写
上篇介绍继承提到过重写,就是子类对父类不满意时需要有自己的拓展,就对父类进行重写,重写的语法介绍一下
重写的方法名称,参数列表(个数,类型,顺序)必须跟父类一致
子类的返回值类型不大于父类
子类的访问权限不小于父类
子类抛出的异常不大于父类
//伪代码,测试用的
public class A {
/*
public C test() { 这段代码会报错,子类返回值类型大于父类了
return null;
} */
public A test() throws Exception {
return null;
}
}
class B extends A {
/*
private B test() { 这段代码会报错,子类访问权限小于父类了
return null;
} */
@Override
public B test() {
return null;
}
}
class C extends A {
/*
public A test() throws Throwable{ 这段代码会报错,子类抛出的异常大于父类了
return null;
} */
@Override
public A test() throws IndexOutOfBoundsException {
return null;
}
}
多态
多态前面的文章也介绍了,就不再过多赘述!就简单总结一下
多态的概念:调用同一方法,不同的对象执行的结果不同。
多态的前提:继承,重写,父类引用指向子类对象
多态的好处:提高程序的扩展性和通用性
父类和子类对象的转换:由子类到父类,自动转换
由父类到子类,强制转换,一般通过instanceof进行判断
public class Person {
public void test(){
System.out.println("Person");
}
}
class Student extends Person{
@Override
public void test(){
System.out.println("Student");
}
}
class Test {
public static void main(String[] args) {
Person person = new Student(); //向上转型,左边的对象类型>右边的类型,而且person编译阶段是Person类型的
person.test(); //访问的时候只能调用Person类和Object的继承信息,运行的时候是创建了Student对象,执行结果:Student
Student student = (Student) person;//向下转型,左边的对象类型<右边的类型,而且student编译阶段是Student类型的
student.test(); //访问的时候能调用Student类和父类的继承信息
student.show();
System.out.println("------------------------------------");
//Teacher teacher= (Teacher) person;
//teacher.test();编译的时候没问题,也都能调用;但是运行之后发现person创建了Student对象,Teacher和Student类没有继承关系不能转换,所以就报出了ClassCastException异常(类型转换异常)
//稳妥的方式就是用instanceof判断一下,格式:变量名/对象 instanceof 数据类型
if(person instanceof Student){
Student student2 = (Student) person;
student2.test();
}else if(person instanceof Teacher){
Teacher teacher2= (Teacher) person;
teacher2.test();
}
}
}
静态绑定 VS 动态绑定
静态绑定:是针对编译期而言,也就是所谓“编译期绑定”,即在编译时根据参数变量的类型判断应该调用哪个方法。
动态绑定:是针对运行期而言,也就是所谓“运行期绑定”,即在运行的时候,根据引用变量所指向的实际对象的类型来调用方法
重载 VS 重写
重载是一个编译期概念、重写是一个运行期间概念。
重载遵循所谓“静态绑定”,重写遵循所谓“动态绑定”
因为在编译期已经确定调用哪个方法,所以重载并不是多态。反之,重写是多态。
重载只是一种语言特性,是一种语法规则,与多态无关,与面向对象也无关。而Java对多态的定义前提就能看出默认就是动态绑定 。
抽象类
之前提过抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。
抽象类也介绍过,意义就是java的某些类不具备实例化的意义,就可以定义为抽象类。目地就是为子类提供了一个抽象模板
Java中用abstract关键字代表抽象,修饰在类上就意味此类是一个抽象类,修饰在方法上就意味此方法为抽象方法。
下面说说这个关键字的使用。
public abstract class Animal {
String personalityTraits;
public Animal(){
}
public Animal(String personalityTraits) {
this.personalityTraits = personalityTraits;
}
public void Breathe(){
System.out.println("呼吸空气!");
}
//抽象方法不能有方法体
public abstract void eat();
}
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼!");
}
}
public class Panda extends Animal {
public Panda(String personalityTraits) {
super(personalityTraits);
}
@Override
public void eat() {
System.out.println("大熊猫吃竹子!");
}
public void show() {
System.out.println("这只大熊猫的性格:" + personalityTraits);
}
}
public class Test {
public static void main(String[] args) {
//Animal animal=new Animal(); 会报错,抽象类不能进行实例化也就是new
Animal animal=new Cat();
animal.Breathe();
animal.eat();
Panda panda=new Panda("温和");
panda.Breathe();
panda.eat();
panda.show();
}
}
/*
执行结果:
呼吸空气!
猫吃鱼!
呼吸空气!
大熊猫吃竹子!
这只大熊猫的性格:温和
*/
细节:抽象类不能进行实例化
抽象类也是一个类,也可以有普通类的任意成员,比如:普通方法、构造方法、属性等等
抽象类里可以没有抽象方法,但是抽象方法必须定义在抽象类中
抽象方法不能使用private、final 和 static来修饰
子类继承抽象类,对父类的方法实现不用进行更改时,可以不用重写,创建对象时直接调用父类的方法
子类必须重写父类所有的抽象方法,如果没有重写父类的抽象方法,则自己也必须声明为抽象类
public abstract class A {
public abstract void test();
public abstract void show();
}
//class B extends A{} 会报错
abstract class B extends A{}
class C extends B{
@Override
public void test() {}
@Override
public void show() {}
}
抽象类中构造器的作用:给子类初始化对象时一定会先super调用父类的构造器
接口
之前提过接口是相关交互功能点定义的集合,是一个标准/规范,接口代表了一种能力,代码上篇也有
再举个例子
//把手机当做接口
public interface MobilePhone {
//发短信
public abstract void texting();
//打电话
public abstract void call();
//拍照
public abstract void takePictures();
}
上述定义了一个接口,定义了一个发短信和打电话的方法,因为这个方法是供外部实现的而且接口也不是用来实例化的,所以也就用public和abstract对方法进行修饰,其实也是默认public abstract修饰的,所以可以改写成
//把手机当做接口
public interface MobilePhone {
//发短信
void texting();
//打电话
void call();
//拍照
public abstract void takePictures();
}
接口定义是为了可以有实现,若不然定义的这个接口没用途,java中实现接口的关键字为implements
//类实现接口,必须重写接口的所有抽象方法
public class P40 implements MobilePhone{
@Override
public void texting() {
System.out.println("p40可以发短信");
}
@Override
public void call() {
System.out.println("p40可以打电话");
}
@Override
public void takePictures() {
System.out.println("p40可以拍照");
}
}
public class FindX2 implements MobilePhone{
@Override
public void texting() {
System.out.println("FindX2可以发短信");
}
@Override
public void call() {
System.out.println("FindX2可以打电话");
}
@Override
public void takePictures() {
System.out.println("FindX2可以拍照");
}
}
实现MobilePhone接口的实现类就意味有发短信,打电话,拍照这些功能,具体的功能实现诸如:拍照的像素多大,P40和FindX2的实现肯定不一样。
可以这么说,一个类和一个接口有什么关系,就在于这个类是否有这个接口规定的能力/方法,如果有的话就可以看做这个类实现了接口
细节:刚说了接口是一个规范,实现就不能随意写,达到这个效果就必须在接口中是常量和抽象方法。方法是用public abstract修饰的,属性是public static final修饰的,不写默认也是这个。既然是规范也就不能实例化
接口可以多继承多实现,即一个接口可以继承多个接口,一个类可以实现多个接口
//伪代码,仅测试专用
public interface A {
int a = 15;// int a = 15 == public static final int a = 15,而且必须进行初始化
void test();// void test() == public abstract void test()
}
interface B {
void show();
}
interface C extends A, B {
}
class D {}
class E extends D implements A, B {
//实现多个接口,但要重写所有接口的抽象方法
@Override
public void test() {}
@Override
public void show() {}
}
class Test {
public static void main(String[] args) {
System.out.println(A.a);//访问接口中的常量,接口名.常量名
}
}
上面接口的使用其实是jdk8版本之前的,之后版本和8版本的有新特性,之后单独一篇文章说一下
内部类
顾名思义就是类的内部还定义了一个类,是类的第五大成员,内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系
内部类分类:成员内部类、静态内部类、局部内部类、匿名内部类
public class Outer {
private int id = 20;
private static String feature = "少年感";
private int local = 1;
public int getId() {
MemberInnerClass innerClass = new MemberInnerClass();
System.out.println(innerClass.id); // 执行结果:0,外部类访问成员内部类需要先创建对象
StaticInnerClass staticInnerClass = new StaticInnerClass();
staticInnerClass.show();//外部类访问静态内部类同样需要先创建对象
new Outer().testLocalInnerClass();//调用外部类的方法执行创建静态内部类对象代码
return id;
}
public static void a() {
System.out.println("外部类:a");
}
public void testLocalInnerClass() {
int local = 2;
//局部内部类,可以把它看做特殊的局部变量,只不过类型是class,作用域为当前方法,不能有访问修饰符,但是可以使用final,参考局部变量
class LocalInnerClass {
int local = 3;
public void show() {
int local = 4;
// 演示内部类属性和外部类重名的情况,会优先访问内部类就近属性
System.out.println("show方法:" + local); //可以直接访问外部所有的属性和方法,执行结果:4
System.out.println("LocalInnerClass:" + this.local); //执行结果:3
System.out.println("Outer:" + Outer.this.local); //执行结果:1
a();
}
}
//外部访问要在内部类之外方法里创建对象,以供外部调用此外部方法执行内部类信息 [作用域]
LocalInnerClass localInnerClass = new LocalInnerClass();
localInnerClass.show();
System.out.println("testLocalInnerClass方法:" + local); //执行结果:2,如果放在局部内部类里,因为作用域的问题,不可能访问到
//匿名内部类相当于特殊的局部内部类,只不过没有class,而且这个类定义的时候,就创建了对象
new AnonymousInnerClass() {
@Override
public void method() {
System.out.println("AnonymousInnerClass-----------------------------------------------");
// ps:匿名内部类只能访问final修饰的局部变量,jdk7版本必须手动添加final,但jdk8版本自动添加了,无需手动添加
System.out.println("testLocalInnerClass方法:" + local); //执行结果:2, 因为作用域的问题能访问所属方法定义的局部变量
System.out.println("Outer类:" + Outer.this.local);//可以直接访问外部所有的属性和方法,执行结果:1
}
}.method();
//原先的方式可能还得先创建一个类实现AnonymousInnerClass接口,然后再创建对象
AnonymousInnerClass innerClass = new AnonymousInnerClass() {
@Override
public void method() {
System.out.println("通过多态方式引用匿名内部类对象");
}
};
innerClass.method();
}
//成员内部类,可以把它看做特殊的成员实例,只不过类型是class,修饰符可以为:private,default,protect,public,final,abstract,作用域为整个类
public class MemberInnerClass {
private int id;
private String name;
public void show() {
int id = 30;
//成员内部类,可以直接访问外部所有的属性和方法
System.out.println(feature); //null
getId();
// 演示内部类属性和外部类重名的情况,会优先访问内部类就近属性
System.out.println("show方法 id:" + id); //执行结果:30
System.out.println("MemberInnerClass id:" + this.id); //执行结果:0
System.out.println("Outer id:" + Outer.this.id); //执行结果:20
}
}
//静态内部类,可以把它看做特殊的静态成员,修饰符同样可以为:private,default,protect,public,final,abstract,作用域为整个类
static class StaticInnerClass {
private String feature = "冷酷";
public void show() {
String feature = "高冷";
//System.out.println(id); 会报错,不能直接访问外部类的非静态信息
// 演示内部类属性和外部类重名的情况,会优先访问内部类就近属性
System.out.println("show方法 feature:" + feature); //执行结果:高冷
System.out.println("StaticInnerClass feature:" + new StaticInnerClass().feature); //执行结果:冷酷
System.out.println("Outer feature:" + Outer.feature); //执行结果:少年感
a();
}
}
public static void test(AnonymousInnerClass innerClass){
System.out.println("接口作为形参------");
}
}
//测试匿名内部类,匿名内部类的用处就是此接口或者抽象类后续只会创建一个对象,而且逻辑代码也不复杂
interface AnonymousInnerClass {
void method();
}
public class TestInnerClass {
public static void main(String[] args) {
//创建外部类对象
Outer outer = new Outer();
//根据外部类对象创建成员内部类对象
Outer.MemberInnerClass memberInnerClass = outer.new MemberInnerClass();
//直接创建成员内部类对象
Outer.MemberInnerClass memberInnerClass2 = new Outer().new MemberInnerClass();
memberInnerClass2.show();
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%");
//创建静态内部类对象
Outer.StaticInnerClass staticInnerClass = new Outer.StaticInnerClass();
staticInnerClass.show();
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
//局部内部类
outer.testLocalInnerClass();
//创建匿名内部类对象,格式:new 接口/类名(参数列表){类体};只适合于该接口只使用一次的应用场景
new AnonymousInnerClass() {
@Override
public void method() {
System.out.println("methodA-------------");
}
}.method();
Outer.test(new AnonymousInnerClass() {
@Override
public void method() {
System.out.println("匿名内部类作为实参");
}
});
}
}