1、访问控制
对象中的属性和方法,是可以根据指定的修饰符来进行访问的。
1.1、概述
类中的属性和方法的四种访问控制:
- public,公共的,在所有地方都可以访问
- protected,受保护的,当前类中,子类中,同一个包中其他类可以访问,不同包中子类,并且创建子类类型的对象,通过子类类型对象可以访问。
- default,默认的,当前类中,同一包中的子类中可以访问,注意,default默认的,指的是空修饰符,并不是default这个关键字,除了在接口中要写出来,在其他类中默认不用写出来
- private,私有的,只有当前类可以访问
修饰符 | 类中 | 同包非子类 | 同包子类 | 不同包子类 | 不同包非子类 |
---|---|---|---|---|---|
public | Y | Y | Y | Y | Y |
protected | Y | Y | Y | N/Y | N |
default | Y | Y | Y | N | N |
private | Y | N | N | N | N |
在正常情况之下,编写一个类,只可以使用public和default修饰
但是,如果这个类是内部类的化,则可以使用这四种权限控制修饰符修饰。
2、内部类
在一个类中在定义一个类
class A{
class B{
}
}
内部类一共分为四种形式
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
如果在A类中定义了一个B类,那么A类就称为外部类,B类称为内部类
2.1、成员内部类
在类中,可以定义成员方法,成员变量,还有成员内部类
class A{
private int age;
public static String name;
public void show(){}
public static void say(){}
class B{
private int id;
public void test(){}
}
}
成员内部类中不可以编写静态的属性和方法
我们知道一个类就会编译后就会产生一个class文件,所以上面这个代码编译通过之后会产生两个class文件,分别对应外部类和内部类
- 外部类:A.class
- 内部类:A$B.class
-
成员内部类和外部类和其他类之间的访问
-
内部类访问外部类
- 内部类可以访问自己类中的属性
- 内部类可以访问外部类的静态属性和静态方法,通过外部类名.静态成员
- 内部类也可以访问外部类的非静态方法和属性,通过外部类名.this.非静态成员
-
外部类访问内部类
- 外部类访问内部类首先要创建内部对象(不一定要在外部类方法中创建),但是不能在静态方法中创建内部类对象和调用内部类对象的方法和属性
- 创建对象之后,可以通过对象直接访问内部类对象里面的方法和属性
-
其它类访问内部类
如果这个成员内部类不是private修饰的,那么在其他类中就可以访问到这个内部类,不论是内部类访问外部类还是外部类访问内部类,如果存在private修饰的成员,也是可以访问,因为在本质上这还是同属于一个类,内部类只是外部类的一个成员
-
只要不是 private修饰的,那么在其他类中就可以访问
class A{ class B{ int s; String s1; public void test(){ } } } public class Test{ public static void main(String[] args){ A a = new A(); B b = a.new B(); System.out.println(b.s); System.out.println(b.s1); b.test(); B b = new A().new B(); new A().new B().test(); } }
-
这是在其他类访问内部类的方法
- 这个内部类是需要导入指定的内部类才可以使用,import 限定类名.内部类
- 在创建对象的时候,首先要创造外部类的对象,然后再使用外部类对象来创建内部类对象
- 形式:外部类对象.new 内部了对象();
注意,内部类也是可以继承其他类,本质上和普通类继承类一致
-
-
2.2、静态内部类
静态内部类和成员内部类是类似的,只是多了一个static修饰
class A{
static class B
}
静态内部类中可以编写静态的属性和方法,当然也可以编写非静态的属性和方法
在编译的时候同样也会产生对应数量的class文件,与成员内部类相同
-
静态内部类和外部类和其他类之间的访问
-
静态内部类访问外部类
- 内部类可以访问自己内部类的静态属性和静态方法,也可以访问非静态的属性和方法
- 在静态内部类中只可以访问外部类的静态属性和静态方法,直接使用 外部类名.静态成员
- 在静态内部类中访问不了外部类中的非静态属性和方法(可以通过创建外部类对象访问)
-
外部类访问静态内部类
- 如果外部类访问的是内部类的静态属性和方法,那么可以直接使用 内部类名.静态成员
- 如果外部类访问的是内部类的非静态属性和方法,就需要通过创建对象去访问
-
其他类中访问内部类
-
其他类访问静态内部类,只要不是被private修饰,那么就访问这个内部类,而且只可以访问非private修饰的成员
-
这个内部类需要import导入,并且是
外部类.内部类
的形式导入。 -
在创建对象的时候,直接使用这个静态内部类的名字即可:
new 静态内部类对象();
,不再需要依赖外部类对象了。class A{ static class B{ public static int s; public void test(){} } } public class Test { public static void main(String[] args){ A a =new A.B(); a.test(); new A.b().test(); } }
-
-
类中可以嵌套接口,接口的内部也可以嵌套其他接口
成员内部类不可以被继承 静态内部类可以被继承
2.3、局部内部类
局部内部类,是另一种形式的内部,在声明在外部类的方法中,相当于方法中的局部变量的位置,它的作用范围只是在当前方法中。
局部内部类基本上用不到,是很不常用的一种类
class A{
public void test(){
class B{
}
}
}
-
局部内部类和外部类,其他类之间的访问’
public class JUbu { public static void main(String[] args) { new A1().test("s");//通过创建外部类对象来调用方法,并且在方法中创建了内部类对象 } } class A1{ static int a =1; private int b =3; public static void t() { } public void test(String s) { class B{ private String name = "ll"; public void kk() { int a =2; final int c = 4; System.out.println(name+"内部"); System.out.println(a);//调用的是离得近的属性 System.out.println(b);//可以访问外部类非静态属性 System.out.println(c);//当访问局部变量时,必须要是常量,不能进行二次赋值 System.out.println(A1.a);//指定访问外部类变量 t();//可以调用外部类方法,不论是否是静态 } } new B().kk(); //代码是一行一行执行,不能没定义这个类就创建这个类,所以必须要放在这个定义类后面创建对象 //外部类访问内部类只能通过调用方法,并且要在方法中且类定义的下面创建对象访问 } } //局部内部类不能定义静态 //编译后产生两class文件 //A.class /*A$1B.class * */
如果这个包含内部类的方法是静态方法,那么内部类访问外部类的时候,只能访问静态的成员。
局部内部类,只能在当前声明的方法中进行使用。
2.4、匿名内部类
匿名内部类,是一种没有名字的内部类,它是内部类的一种简化写法。是Java中一个很重要的内部类
在普通代码中,使用一个接口的步骤:
- 声明一个实现类去实现这个接口
- 实现接口里面的所有抽象方法(重写)
- 在其他代码中,创建这个实现类的对象
- 调用这个类中重写后的方法
这是常规的方法,而匿名内部类就是将这一个接口实现的过程简化
父类(抽象类)或者接口类型 变量名 = new 父类(抽象)或者接口(){
// 方法重写
@Override
public void 需要重写的方法名() {
// 执行语句
}
};
变量名.方法名();
匿名内部类的两种形式
- 利用一个父类(抽象类),进行声明并创建匿名内部类对象,这个匿名内部类默认就是这个父类(抽象类)的子类型
- 利用一个接口,进行声明并创建匿名内部类对象,这个匿名内部类默认就是这个接口的实现类
这就体现了多态的作用,父类引用指向子类对象
匿名内部类是没有名字的:
- 匿名类必须依托于一个父类型(抽象类)或者一个接口
- 匿名内部类在声明的同时,就必须创建出对象,否则后面就没法创建了
- 匿名内部类中无法定义构造器
父类型(抽象)来声明并创建匿名内部类对象
public abstract class Animal {
public abstract void run();
}
class Test{
public static void main(String[] args) {
Animal animal = new Animal(){
@Override
public void run() {
System.out.println("匿名内部类中的默认实现");
}
};
animal.run();
利用接口来声明并创建匿名内部类对象
public interface Action {
void run();
}
class Test{
public static void main(String[] args) {
Action a = new Action(){
@Override
public void run() {
System.out.println("匿名内部类中的默认实现");
}
};
a.run();
}
}
注意,在匿名内部类中必须要重写方法,这就表示,父类还是接口中必须要有相同的方法,不然编译报错;因为匿名内部类也是定义在方法之中,所以在访问局部变量的时候必须是一个常量,而成员变量则无所谓,其他限制等与局部内部类类似
3、包装类
Java的基本数据类型,只能表示数值,不是对象,所以不能用来调用方法
3.1、概述
为了调用更方便Java操作和调用相应类型的方法,javaAPI又专门提供了对应的类类型,这是就变成对象了,就可以调用方法或者访问属性了
基本类型 | 包装类型 |
---|---|
boolean | java.lang.Boolean |
byte | java.lang.Byte |
short | java.lang.Short |
int | java.lang.Integer |
long | java.lang.Long |
float | java.lang.Float |
double | java.lang.Double |
char | java.lang.Character |
3.2、自动装箱,拆箱
在Jdk1.5或以上的版本,可以支持基本类型和包装类型之间的自动装箱,拆箱
Integer i = 0;//自动装箱,将int 类型的 0 包装成为 Integer类型
Integer a = new Integer(1);
int i = a;//自动拆箱,将Ingeger类型的a转化成 int类型的,并把数值1传给变量i
4、Object中常用的方法
Object类是所有类的父亲型,类中定义地方法,java中所有地对象都可以调用
4.1、toString
toString方法可以返回一个对象默认字符串形式
Object中提供地toString默认示返回一个对象的地址
在默认情况之下,println方法会调用这个对象的toString方法
如果没有重写toString方法的情况下,默认不要自己手动在对象后面添加toString方法
子类可以对该方法进行重写
4.2、getClass方法
getClass方法是非常重要的一种方法,他可以返回一个引用在运行时所指向的对象,具体类型是什么。
该方法是native修饰的本地方法,这就代表着这不是Java语言实现
该方法不能被重写,调用者一定是Object中的getClass方法
4.3、equals
该方法可以比较两个对象是否相等
而我们平时经常使用的==,是比较两个对象地址值和字面值是否都相等
子类可以对该方法进行重写
重写equals方法需要注意的点:
- 自反性:对任意引用obj,obj.equals(obj)的返回值一定为true
- 对称性:对于热河引用a1,a2,当且仅当a1.equals(a2)返回值为ture时,a2.equals(a1)返回值也为ture
- 传递性:如果a1.equals(a2)为true,a2.equals(a3)也为true,则a1.equals(a3)也一定为true
- 一致性:如果参与比较的对象没任何改变,则对象比较的结果也不应该有任何改变
- 非空性:任何非空的引用obj,obj.equals(null)的返回值一定为false