文章目录
- 前言
- 一 抽象类
- 1.1 抽象类的概念
- 1.2 抽象类的语法:
- 1.3 抽象类与普通类的区别:
- 二 接口
- 2.1 接口的概念
- 2.2 接口的语法
- 2.2.1 接口的各个组成
- 2.2.2 接口之间的继承
- 2.3 接口的实现
- 接口不可以实例化对象
- 2.4 接口实现多态
- 三 Object类
- 3.1 Object类是什么?
- 3.2 关于Object类中的几个方法:
- 3.2.1 equals方法
- 3.2.2 hashcode方法
- 3.2.3 toString方法
- 四 内部类
- 4.1 内部类的概念
- 4.2 几种内部类
- 静态内部类:
- 实例内部类:
- 局部内部类
- 匿名内部类
- 外部类对几种内部类访问的情况:
- 外部类对静态内部类的访问:
- 外部类对实例内部类的访问:
- 外部类对局部内部类的访问:
前言
一 抽象类
1.1 抽象类的概念
抽象类是一种特殊类,它为子类提供一个的通用的模版,使得子类继承
对于抽象类所描述的事物,现实世界中是不存在的,比如动物,现在世界中不存在动物这个实体!
1.2 抽象类的语法:
abstract:
抽象类由abstract关键字修饰,放置在class关键字之前:
举例:
abstract class Animal{
}
抽象方法:抽象方法是指由abstract关键字修饰的方法,它不可以有方法体。抽象方法只能存在于抽象类中。
抽象方法与抽象类的关系是:有抽象方法的类一定是抽象类,抽象类中不一定有抽象方法。
abstract class Animal{
public int a ;
public static void b (){
System.out.println("抽象类当中的静态方法");
}
//此抽象方法并没有方法体!
abstract void shout();
}
在抽象类被子类继承之后,如果子类不是抽象类,那么本抽象类的抽象方法必须被实现重写,重写成有方法体的方法。
abstract class Animal{
public int a ;
public static void b (){
System.out.println("抽象类当中的静态方法");
}
abstract void shout();
}
abstract class Dog extends Animal{
@Override
void shout() {
System.out.println("狗发出汪汪叫");
}
}
抽象类不能够被实例化对象:
因为现实世界中,并不存在抽象类对应的事物,它是一个概念,所以不能被实例化对象!
1.3 抽象类与普通类的区别:
abstract class Animal{
public int a ;
public void eat(){
System.out.println("动物正在吃饭");
}
public static void b (){
System.out.println("抽象类当中的静态方法");
}
abstract void shout();
}
我们发现抽象类除了不能够实例化对象外,其他功能与普通类并无差异,我们也可以不实例化父类的对象,为什么还要引进抽象类呢?
因为这样可以有效地避免错误,当我们并没有重写父类中的抽象方法,
或者实例化了父类的对象,这时编译器会报错,这相当于编译器为我们加了一层保险。
(有时父类不需要进行实例化对象且不能引用父类原有的方法,需要重写)
二 接口
2.1 接口的概念
//接口在日常生活中有插座的接口,有电脑USB的接口,这些接口都遵循着各自统一的标准
//这体现了在一堆设备(比如一些电脑)中有着共同的标准。
//在java中
//与类一样,接口也是一种引用数据类型
// 接口的思想即一些类公共的行为规范。
// 接口的含义是:具有....的特性
// 比如会游泳——狗,鱼等,这个行为是这两个类共有的特性。
举例:
接口的名称一般已**I**为首字母!
public interface ISwiming {
void func1();
}
2.2 接口的语法
2.2.1 接口的各个组成
interface关键字:
接口的定义格式与类的定义格式并无区别,唯一的不同之处在于关键字class换成关键字interface
接口中的成员变量:
接口中的成员变量默认是由 public static w7final关键字修饰的,且必须进行初始化。
public interface ISwiming {
public static final int a = 10 ;
}
因为java默认接口中的成员变量被public static final修饰,所以在编写代码时
可以省略掉前面的三个词
接口中的方法:
接口中的方法除default关键字修饰的与static修饰的之外,其他的方法均由public abstract修饰。
抽象方法:
因为除default关键字修饰与static关键字修饰之外的方法,编译器均默认为public 修饰的抽象方法(不能替换成别的修饰符),所以我们可以简写成:
void func1();
静态方法:
在接口中的静态方法默认被public修饰,不能换成别的修饰符
注意:
接口在创建时,尽量遵循代码简洁的原则,能冗余的修饰符都省略掉!
default修饰的方法:
在jdk8以后,接口中default修饰的方法可以有方法体!
注:
接口中不允许有构造方法!
2.2.2 接口之间的继承
对于类只能继承一个类,接口可以继承多个接口
举例:
interface A{
}
interface B{
}
interface C extends A,B{
}
2.3 接口的实现
一个接口的实现需要有相应实现的类。
类实现接口时的关键字为implements
位于类名与接口名之间!
举例:
interface A{
void func1();
}
interface B{
void func2();
}
interface C extends A,B{
void func3();
}
class test1 implements C{
@Override
public void func3() {
}
@Override
public void func2() {
}
@Override
public void func1() {
}
}
代码解析:
在上面的代码中,test类,实现了接口C,
类在实现接口时,必须重写实现接口中的抽象方法
所以必须实现func3()方法,由于接口C继承了接口A与接口B,所以test类还需要实现func1()与func2()方法!
一个类可以实现多个接口
interface A{
void func1();
}
interface B{
void func2();
}
class test1 implements A ,B{
@Override
public void func2() {
}
@Override
public void func1() {
}
}
接口不可以实例化对象
但是接口可以接收实现类的对象:
interface A{
void func1();
}
interface B{
void func2();
}
interface C extends A,B{
void func3();
}
class test1 implements C{
@Override
public void func3() {
}
@Override
public void func2() {
}
@Override
public void func1() {
}
}
public class Test {
public static void main(String[] args) {
/* C c = new C() ;*/
C c = new test1();
}
}
2.4 接口实现多态
//创建一个关于游泳的接口
interface ISwiming2{
void func1();
}
//创建一个Dog类实现接口
class Dog implements ISwiming2{
String name;
public Dog(String name) {
this.name = name;
}
public void func1(){
System.out.println( this.name+ "会游泳");
}
}
//创建一个Fish类,实现接口
class Fish implements ISwiming2{
String name;
public Fish(String name) {
this.name = name;
}
public void func1(){
System.out.println( this.name+"会游泳");
}
}
//测试类
public class Test {
//创建一个静态方法,参数为接口类型 ,用于接受实现了接口的类的对象
public static void func2(ISwiming2 iSwiming2){
iSwiming2.func1();
}
public static void main(String[] args) {
Dog dog = new Dog("旺财");
func2(dog);
Fish fish =new Fish("小鱼儿");
func2(fish);
}
}
三 Object类
3.1 Object类是什么?
Object类是java中提供的类,它可以说是所有的类父类,所有的类都继承Object类
Object是开发中最高统一类型的形参。
例如:下面的代码中,虽然Dog的直属父类是Animal,但是Animal默认继承了Object类,所以Dog类也继承了Object类
class Animal{
}
class Dog extends Animal{
}
3.2 关于Object类中的几个方法:
Object中的方法只有这些,这里先主要了解画横线的三个方法,初步了解。
3.2.1 equals方法
equals方法用于两个对象间的比较
在java中,Object类的equals方法默认为:
此方法只能比较基本数据类型,而不能比较引用数据类型(因为只能比较地址),
为了比较引用数据类型,需要对方法进行重写
代码:
class Dog {
String name;
int age;
public Dog(String name,int age) {
this.name = name;
this.age = age;
}
//对equals方法的重写!
@Override
public boolean equals(Object o) {
//Object需要先进行强制类型转换
Dog tmp = (Dog) o;
//分别比较年龄与名字!
return tmp.age == this.age
&& tmp.name.equals(this.name);
}
}
public class Test {
public static void main(String[] args) {
Dog dog1 = new Dog("旺财",10);
Dog dog2 = new Dog("旺财",10);
System.out.println(dog1 == dog2);
System.out.println(dog1.equals(dog2));
}
}
3.2.2 hashcode方法
hashcode方法用于算出对象具体的地址,这涉及到数据结构,因为还没学到数据结构,我们暂时把它当做一个内存地址。
代码:
class Dog {
String name;
int age;
public Dog(String name,int age) {
this.name = name;
this.age = age;
}
}
public class Test {
public static void main(String[] args) {
Dog dog1 = new Dog("旺财",10);
Dog dog2 = new Dog("旺财",10);
System.out.println(dog1.hashCode());
System.out.println(dog2.hashCode());
}
}
对于两个对象结果得出的值不相同,假如对于相同名字与年龄的对象,我们想得出同样的值,此时该怎么办?重写方法:
原本Object类中的hashcode方法:
此方法的修饰符有native,说明此方法的实现是由c与c++语言实现的,我们无法查看它是怎样实现的。
重写后的方法:
public int hashCode() {
//根据名字与年龄属性来判断哈希值
return Objects.hash(name, age);
}
此时两个对象的哈希值就相同了!
3.2.3 toString方法
Object中的toString类实现:
这个方法用于打印引用类型变量的相关值
举例:
public class Test {
public static void main(String[] args) {
//创建一个整型数组
int [] arr1 = {1,2,3,4,5};
System.out.println(arr1);
System.out.println(arr1.toString());
}
}
结果相同:如果我们想直接打印数组中的内容怎么办?
java提供的Arrays类中重写了Object类中的toString方法:
public static String toString(int[] a) {
if (a == null)
return "null";
int iMax = a.length - 1;
if (iMax == -1)
return "[]";
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
b.append(a[i]);
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
重写后的方法可以将将数组中的内容以字符串中的形式打印出来!
public class Test {
public static void main(String[] args) {
int [] arr1 = {1,2,3,4,5};
System.out.println(arr1);
System.out.println(arr1.toString());
//调用重写之后的方法:
System.out.println(Arrays.toString(arr1));
}
}
四 内部类
4.1 内部类的概念
//内部类是定义在另一个类内部的类。
相对的,包含内部类的类,我们称为外部类
4.2 几种内部类
//内部类有四种
//静态内部类
//实例(构造)内部类
//局部内部类
//匿名内部类
静态内部类:
静态内部类的格式:静态内部类定义在外部类中,与外部类的成员变量,方法同级。
静态内部类可以看做是外部类的一个静态成员,所以在静态内部类实例化时,
不需要先实例化外部类!
在class关键字前被static关键字修饰。
class Dog{
String name;
int age;
// 静态内部类的创建!
static class Inner{
String name;
public void shout(){
System.out.println("我是静态内部类");
}
}
}
关于静态内部类的语法:
(1)
静态内部类可以定义自己的成员变量,成员方法,静态方法,在类的组成方面与普通类没有区别!
static class Inner{
String name;
public void shout(){
System.out.println("我是静态内部类");
}
public static void func1 (){};
}
(2)静态内部类的实例化:
Dog.Inner inner = new Dog.Inner();
静态内部类可直接实例化,其中Dog.Inner 是个整体,Dog.Inner() 也是一个整体
(3)静态内部类访问外部类成员问题:
1)
静态内部类不能直接访问外部类的非静态成员变量与方法。
(因为非静态成员变量与方法依赖于对象!)
public class Test {
public static void main(String[] args) {
Dog.Inner inner = new Dog.Inner();
inner.func1();
}
}
class Dog{
public static String sex = "男" ;
String name = "李四";
int age;
// 静态内部类的创建!
static class Inner{
public static void func1 (){
System.out.println(name);
};
}
}
2)
静态内部类可以访问外部类中的静态成员与静态方法!
(因为外部类中的静态成员与静态方法不依赖于对象,而依赖于类)
public class Test {
public static void main(String[] args) {
Dog.Inner inner = new Dog.Inner();
inner.func1();
}
}
class Dog{
public static String sex = "男" ;
String name = "李四";
int age;
// 静态内部类的创建!
static class Inner{
public static void func1 (){
System.out.println(sex);
};
}
}
3)
静态内部类不能使用外部类的this关键字(因为this关键字依赖于对象,不依赖于类)。
实例内部类:
实例内部类是定义在一个类内部,且不是静态内部类的类。
实例内部类可以看做是外部类的一个非静态成员。
实例内部类的实例化前,必须先实例化外部类对象。
1)实例内部类的实例化:
public class Test {
public static void main(String[] args) {
//先实例化外部类对象后:
Dog dog1 = new Dog();
//在外部类对象中,实例化内部类(实例内部类)对象
Dog.Test2 test2 = dog1. new Test2();
test2.func1();
}
}
class Dog{
String name;
int age;
//实例内部类
public class Test2{
int a ;
public void func1(){
System.out.println("实现了实例内部类");
}
}
}
代码分析:
Dog.Test2 test2 = dog1. new Test2();
Dog.Test2 是一个整体,代表内部类类名,test2是内部类对象名称
dog1是外部类对象,后面跟new Test2()意味着在dog1对象中实例化内部类对象!
2)实例内部类访问外部类的成员:
因为实例内部类可以看做是外部类的一个非静态成员,
所以实例内部类可以直接访问外部类中所有的属性与方法。
public class Test {
public static void main(String[] args) {
Dog dog1 = new Dog();
Dog.Test2 test2 = dog1. new Test2();
test2.func2();
}
}
class Dog{
String name = "王五";
int age = 10;
public static int b = 5;
private void test1(){
System.out.println("尝试访问外部类中方法");
}
public class Test2{
int a ;
public void func2(){
//访问外部类中的name与age属性
System.out.println("我的名字是"+name+", 年龄是"+age);
//返回外部类中的静态属性b
System.out.println(b);
//访问外部类中的方法
test1();
}
}
}
结果表明,全部访问成功!
3)实体内部类中this关键字:
this关键字本质上就是当前引用的对象,在实体内部类中,
this既可以指向自己,又可以指向外部类(因为实体内部类可以看作是外部类的一个成员)
这相当于有两个不同的this。
当调用实例内部类中的成员时,用this.x(x代表成员名),
当调用外部类中的成员时,用外部类名.this.x (x代表成员名)
举例:
class Dog{
String name = "王五";
int age = 10;
public static int b = 5;
private void test1(){
System.out.println("尝试访问外部类中方法");
}
public class Test2{
String name = "张三";
int age = 9;
int a ;
public void func2(){
//直接使用this可以访问内部类中的成员
System.out.println("我的名字是"+this. name+", 年龄是"+ this.age);
//那如何通过this访问到外部类中的成员呢?
System.out.println("我的名字是"+ Dog. this. name+", 年龄是"+ Dog.this.age);
}
}
}
局部内部类
局部内部类是定义在外部类的方法或代码块中的类,可以把它看做一个局部变量
局部内部类的语法格式与普通类相同。
局部内部类的实例化:
局部内部类只能在所在的方法或代码块中进行实例化,而不能去其他位置进行实例化:
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.func1();
}
}
class Dog{
String name = "张三";
public void shout(){
System.out.println("狗发出汪汪叫");
}
public void func1(){
class Test3{
int a ;
public void func2(){
System.out.println("我的名字叫"+name);
}
}
//只能在所在的方法中实例化对象
Test3 test3 = new Test3();
test3. func2();
}
}
局部内部类对外部类中成员的引用:
局部内部类可以引用外部类中所有的成员!
举例:
引用name时,编译器并不报错!
匿名内部类
匿名内部类的格式:
new 类型 (){
重写方法;
};
//类型可以是普通类,抽象类,接口
匿名内部类格式的由来:
我们在继承一个类或者实现一个接口时,往往需要新建一个类:
//创建一个Animal接口
interface Animal{
void shout();
}
//创建一个Dog类,实现Animal接口
class Dog implements Animal{
public void shout(){
System.out.println("狗发出汪汪叫");
}
}
//创建测试类
public class Test {
public static void main(String[] args) {
Dog dog1 = new Dog();
dog1.shout();
}
}
当我们只需要创建一次对象时,这种方法就比较麻烦,
更多的是当我们需要实现一个接口时,并不想创建一个实现类,而只是想实现接口中
的某一方法时,单纯创建一个实现类过于麻烦。
因为上述的两种情况,java中引入了匿名内部类的概念。
匿名内部类举例:
匿名内部类在创建时,即已经进行实例化对象,
在实现接口时,可以通过接口引用来接收对象
在实现抽象类时,也可以通过接口引用来接收对象
在继承普通类时,则不能通过普通类引用来接收对象
//创建一个Animal接口
interface Animal{
void shout();
}
//创建一个Test类
public class Test {
public static void main(String[] args) {
//使用Animal引用来接收匿名内部类所创建的对象
Animal a = new Animal(){
public void shout(){
System.out.println("狗发出汪汪叫");
}
};
a.shout();
}
}
也可以直接在匿名内部类后引用成员:
new Animal(){
public void shout(){
System.out.println("狗发出汪汪叫");
}
}.shout();
像这样,接口的实现类,子类,我们用到了,但是类名是什么,我们是不知道的,
它是在需要的地方直接定义和实例化的,因此它是“匿名”的
因为匿名内部类在创建时本身就在实例化,所以匿名内部类的创建可以在 可以创建
对象 的任何位置进行创建。
外部类对几种内部类访问的情况:
外部类对静态内部类的访问:
对于静态内部类中静态的成员变量与方法,外部类可以通过静态内部类类名进行调用。
对于非静态成员,外部类则需要实例化静态内部类对象进行调用!
class Animal{
String name;
public void test(){
//调用静态内部类中的func1方法
Test.func1();
//调用静态内部类中的age属性
System.out.println(Test.age);
}
static class Test{
static int age;
public static void func1(){
System.out.println("静态内部类中的静态成员方法");
}
}
}
//创建测试类
public class Test {
public static void main(String[] args) {
Animal animal = new Animal();
animal.test();
}
}
外部类对实例内部类的访问:
外部类对于实例内部类中的静态成员访问时,也需要实例内部类类名调用。
对于实例内部类中非静态成员访问时,需要先创建实例内部类对象,再进行调用(在外部类中创建实例内部类对象时,不需要先创建外部类对象)
class Animal{
String name;
public void test2(){
System.out.println(Test2.a);
Test2 test2 = new Test2();
test2.shout();
}
public class Test2{
static int a = 10;
public void shout(){
System.out.println("调用实例化中的非静态方法");
}
}
}
public class Test {
public static void main(String[] args) {
Animal animal = new Animal();
animal.test2();
}
}
外部类对局部内部类的访问:
前面我们讲过局部内部类可以看作外部类的一个局部变量,局部内部类的作用域仅限于其所在的代码块或方法中,因此外部类无法在该代码块之外访问局部内部类
外部类对局部内部类的访问,只能在局部内部类所在的方法中进行访问,前提是局部内部类的对象已经创建。
class Animal{
public void func1(){
//创建一个局部内部类
class Test3{
static int a ;
public void func2(){
System.out.println("调用局部内部类中非静态方法");
}
}
Test3 test3 = new Test3();
//只能在局部内部类所在的方法中进行访问成员
test3.func2();
}
}
public class Test {
public static void main(String[] args) {
Animal animal = new Animal();
animal.func1();
}
}