1.类、对象、成员变量和成员方法的理解
- 类:一组属性和行为的集合、是一个抽象的概念,一个类可以创建多个不同的对象
- 对象: 类的具体表现形式,具体存在的个体
- 成员变量: 事物的属性
- 成员方法: 事物的行为
1.1 成员变量和局部变量的区别
A:在类中的位置不同
- 成员变量:类中方法外
- 局部变量:代码块,方法定义中或者方法声明上(方法参数)
B:在内存中的位置不同
- 成员变量:在堆中
- 局部变量:在栈中
C:生命周期不同
- 成员变量:随着对象的创建而存在,随着对象的消失而消失
- 局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
D:初始化值不同
- 成员变量:有默认值(构造方法对它的值进行初始化)
- 局部变量:没有默认值,必须定义,赋值,然后才能使用
1.2 访问权限修饰符 public、private、protected, 以及不写(默认)时的区别
访问权限 | 类 | 包 | 子类 | 其他包 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protect | √ | √ | √ | |
default | √ | √ | ||
private | √ |
1.3 static 关键字修饰的作用
static方法就是没有this的方法。在static方法内部不能调用非静态方法,
反过来是可以的。而且可以在没有创建任何对象的前提下,
仅仅通过类本身来调用static方法。
这实际上正是static方法的主要用途。 —— 《Java编程思想》P86
static 可以修饰 类的成员方法、类的成员变量,另外可以编写static代码块来优化程序性能,但static 不能修饰类。
1.3.1 静态方法
static 修饰的方法一般叫做静态方法,静态方法不依赖于对象访问,因此没有 this 的概念(this 代表所在类的对象引用),正因如此静态方法能够访问的成员变量和成员方法也都必须是静态的
在静态方法中不能调用动态方法(对象方法),但是在对象方法中可以调用静态方法。
静态方法 存放在 JVM 的方法区,属于线程共享;
1.3.2 静态变量
static 修饰的变量也称作静态变量,静态变量属于类,所以也称为类变量,存储于方法区中的静态区,随着类的加载而加载,消失而消失,可以通过类名调用,也可以通过对象调用。
1.3.3 静态代码块
静态代码块 是类中(方法中不行)使用过static 关键字和 {} 申明的代码块
static {
代码逻辑。。。
}
执行: 静态代码块 在类被加载的时候就执行了,而且只运行一次,并且优先于 各种代码块以及构造行概述
作用: 一般情况下,如果有些代码需要在项目启动的时候就执行,这时候 就需要静态代码块。比如一个项目启动需要加载的 很多配置文件等资源,我们就可以都放入静态代码块中。
1.3.4 构造代码块
概念: 在java类中使用{}声明的代码块(和静态代码块的区别是少了static关键字)
执行: 构造代码块在创建对象时被调用,每次创建对象都会调用一次,但是优先于构造函数执行。
作用: 和构造函数的作用类似,都能对对象进行初始化,并且只创建一个对象,构造代码块都会执行一次。但是反过来,构造函数则不一定每个对象建立时都执行(多个构造函数情况下,建立对象时传入的参数不同则初始化使用对应的构造函数)。
因为每个构造方法执行前, 首先执行构造代码块,所以可以把多个构造方法中相同的代码可以放到这里
2.对象的三大特征
2.1 封装
封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式
2.2 继承
继承就是在一个已有类的基础上派生出新类(例如动物类可以派生出狗类和猫类),子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为
特点:Java只支持单继承,不支持多继承(C++支持),但是Java支持多层继承(继承体系)形象的说就是:儿子继承爸爸,爸爸继承爷爷,儿子可以通过爸爸继承爷爷。
注意:
A: 子类只能继承父类所有非私有成员(成员方法和成员变量)
B:子类不能继承父类的构造方法,但是可以通过super关键字去访问方法
C: 不要为了部分功能而继承(多层继承会使得子类继承多余的方法)
2.3 多态
多态是同一个行为具有多个不同表现形式或形态的能力,例如:黑白打印机和彩色打印机相同的打印行为却有着不同的打印效果,
- 对象类型和引用类型之间存在着继承(类)/ 实现(接口)的关系;
- 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
- 如果子类重写了父类的方法,最终执行的是子类覆盖的方法,如果没有则执行的是父类的方法。
3. 其他
3.1 抽象类和接口的区别
继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如狗是否能钻火圈,能则可以实现这个接口,不能就不实现这个接口。
3.2 内部类
把类定义在另一个类的内部,该类就称为内部类。
举例:把类 Inner 定义在类 Outer 中,类 Inner 就被称为内部类。
class Outer {
class Inner {
}
}
访问规则: 内部类可以方位外部类成员包括私有。外部类想要访问内部类成员,必须创建对象
内部类分类:
- 成员内部类、
- 局部内部类、
- 静态内部类、
- 匿名内部类
3.2.1 成员内部类
就是位于外部类成员位置的类
特点: 可以使用外部类中所有的成员变量和成员方法(包括private的)
格式:
class Outer {
private int age = 20;
// 成员位置
class Inner {
public void show() {
System.out.println(age);
}
}
}
class Test {
public static void main(String[] ages) {
// 成员内部类是非静态的演示
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
成员内部类经典题(填空)
请在三个println 后括号中填空使得输出25,20,18
class Outer {
public int age = 18;
class Inner {
public int age = 20;
public viod showAge() {
int age = 25;
System.out.println(age);//空1
System.out.println(this.age);//空2
System.out.println(Outer.this.age);//空3
}
}
}
3.2.2 局部内部类
就是定义在一个方法或者作用域里边的类
特点:
主要是作用域发生了变化,只能在自身所在方法和属性中被使用
格式:
class Outer {
public void method(){
class Inner {
}
}
}
访问时:
// 在局部位置,可以创建内部类对象,通过对象调用和内部类方法
class Outer {
private int age = 20;
public void method() {
final int age2 = 30;
class Inner {
public void show() {
System.out.println(age);
// 从内部类中访问方法内变量age2,需要将变量声明为最终类型。
System.out.println(age2);
}
}
Inner i = new Inner();
i.show();
}
}
为什么局部内部类访问局部变量必须加final修饰呢
因为局部变量是随着方法的调用而调用,使用完毕就消失,而堆内存的数据并不会立即消失。
所以,堆内存还是用该变量,而该变量已经没有了。为了让该值还存在,就加final修饰。
原因是,当我们使用final修饰变量后,堆内存直接存储的是值,而不是变量名。
(即上例 age2 的位置存储着常量30 而不是 age2 这个变量名)
3.2.3 静态内部类
我们所知道 static 是不能用来修饰类的,但是成员内部类可以看做外部类中的一
个成员,所以可以用 static 修饰,这种用 static 修饰的内部类我们称作静态内
部类,也称作嵌套内部类。
特点:不能使用外部类的非static成员变量和成员方法
解释:非静态内部类编译后会默认的保存一个指向外部类的引用,而静态类却没有。
简单理解:即使没有外部类对象,也可以创建静态内部类对象,而外部类的非static成员必须依赖于对象的调用,静态成员则可以直接使用类调用,不必依赖于外部类的对象,所以静态内部类只能访问静态的外部属性和方法。
class Outter {
int age = 10;
static age2 = 20;
public Outter() {
}
static class Inner {
public method() {
System.out.println(age);//错误
System.out.println(age2);//正确
}
}
}
public class Test {
public static void main(String[] args) {
Outter.Inner inner = new Outter.Inner();
inner.method();
}
}
3.2.4匿名内部类
一个没有名字的类,是内部类的简化写法
本质:其实是继承该类或者实现接口的子类匿名对象
格式:
new 类名或者接口名() {
重写方法();
}
这也就是下例中,可以直接使用 new Inner() {}.show(); 的原因等于 子类对象.show();
interface Inter {
public abstract void show();
}
class Outer {
public void method(){
new Inner() {
public void show() {
System.out.println("HelloWorld");
}
}.show();
}
}
class Test {
public static void main(String[] args) {
Outer o = new Outer();
o.method();
}
}
匿名内部类在开发中的使用
3.2.4.1 为什么使用内部类
作为一个类的编写者,我们很显然需要对这个类的使用访问者的访问权限做出一定的限制,我们需要将一些我们不愿意让别人看到的操作隐藏起来,
如果我们的内部类不想轻易被任何人访问,可以选择使用private修饰内部类,这样我们就无法通过创建对象的方法来访问,想要访问只需要在外部类中定义一个public修饰的方法,间接调用。
public interface Demo {
void show();
}
class Outer {
private class test implements Demo {
public void show() {
System.out.println("密码备份文件");
}
}
public Demo getInner() {
return new test();
}
}
我们来看其测试
public static void main(String[] args) {
Outer outer = new Outer();
Demo d = outer.getInner();
i.show();
}
//运行结果
密码备份文件
这样做的好处之一就是,我们可以在这个public方法中增加一些判断语句,起到数据安全的作用。
其次呢,我们的对外可见的只是 getInner() 这个方法,它返回了一个Demo接口的一个实例,而我们真正的内部类的名称就被隐藏起来了
3.2.4.2 实现多继承
我们之前的学习知道,java是不可以实现多继承的,一次只能继承一个类,我们学习接口的时候,有提到可以用接口来实现多继承的效果,即一个接口有多个实现,但是这里也是有一点弊端的,那就是,一旦实现一个接口就必须实现里面的所有方法,有时候就会出现一些累赘,但是使用内部类可以很好的解决这些问题
public class Demo1 {
public String name() {
return "BWH_Steven";
}
}
public class Demo2 {
public String email() {
return "xxx.@163.com";
}
}
public class MyDemo {
private class test1 extends Demo1 {
public String name() {
return super.name();
}
}
private class test2 extends Demo2 {
public String email() {
return super.email();
}
}
public String name() {
return new test1().name();
}
public String email() {
return new test2().email();
}
public static void main(String args[]) {
MyDemo md = new MyDemo();
System.out.println("我的姓名:" + md.name());
System.out.println("我的邮箱:" + md.email());
}
}
我们编写了两个待继承的类 Demo1 和 Demo2,在 MyDemo类中书写了两个内部类,test1 和test2 两者分别继承了Demo1 和 Demo2 类,这样 MyDemo 中就间接的实现了多继承
3.2.4.3 用匿名内部类实现回调功能
我们用通俗讲解就是说在Java中,通常就是编写一个接口,然后你来实现这个接口,然后把这个接口的一个对象作以参数的形式传到另一个程序方法中, 然后通过接口调用你的方法,匿名内部类就可以很好的展现了这一种回调功能
public interface Demo {
void demoMethod();
}
public class MyDemo{
public test(Demo demo){
System.out.println("test method");
}
public static void main(String[] args) {
MyDemo md = new MyDemo();
//这里我们使用匿名内部类的方式将接口对象作为参数传递到test方法中去了
md.test(new Demo){
public void demoMethod(){
System.out.println("具体实现接口")
}
}
}
}