目录
源文件中有且只能有一个public类,且该源文件的文件名为该public类的类名。
1.代码块
使用 { } 定义的一段代码
- 根据代码块定义的位置以及关键字,又可分为以下四种:
普通代码块 :定义在方法中的代码块
构造块(非静态代码块):定义在类中的代码块(构造一个类的对象时,被执行的代码块),不加任何修饰符。
静态代码块:(static+构造块)
同步代码块
-
1.1 普通代码块
如果方法中代码过长,为避免变量重名,使用普通代码块。
代码示例:
public class CodeBlock{
public static void main(String[] args){
{
int x=10;
System.out.println(x);
}
int x=100;
System.out.println(x);
}
}
-
1.2 构造块
在对象产生时,优先于构造方法执行。有几个对象产生,就调用几次构造块。
用于在构造方法执行前完成一些属性的初始化操作。
-
1.3 静态代码块
- 静态块的主要作用是为static属性进行初始化
- 静态块优先于构造块执行,优先于主方法(main)执行。无论产生多少实例化对象,静态块都只执行一次。
原因:第一次使用这个类的时候,将该类编译完成的字节码,加载到JVM中解释执行。完成后,JVM中已包含该类。将类中的静态块都执行一遍。因此,静态块在创建对象之前执行,且只执行一次。
-
当代码中只有一个静态块和一个没有方法体(不创建对象)的main()时,静态块仍执行
∵main()是类中的静态方法,当用到该方法时,必须知道该方法所在类 ∴ JVM找到该类,并运行静态块
代码示例:
public class CodeBlock {
private int codeline;
private String codeFileName;
private int salary;
// 暗示静态块优先于构造块执行
static {
System.out.println("静态块,属于类");
}
//暗示构造块的执行在构造方法之前
{
System.out.println("CodeBlock 非静态块");
codeline = 10;
//假设salary的初始化较为复杂,则可以在此对其进行初始化
salary = codeline * 2;
}
public CodeBlock() {
//也可以在此对其进行初始化
System.out.println("构造方法执行");
}
public static void main(String[] args) {
CodeBlock codeBlock = new CodeBlock();
CodeBlock codeBlock2 = new CodeBlock();
System.out.println("main()");
}
}
}
练习:
class HelloA { //构造方法
public HelloA() {
System.out.println("Hello A!父类构造方法");
} //非静态代码块
{
System.out.println("i'm A class.父类非静态代码块");
} //静态代码块
static {
System.out.println("static A 父类静态代码块");
}
}
class HelloB extends HelloA { //构造方法
public HelloB() {
System.out.println("Hello B! 构造方法");
} //非静态代码块
{
System.out.println("i'm B class.非静态代码块");
} //静态代码块
static {
System.out.println("static B 静态代码块");
}
}
public class Test{
public static void main(String[] args) {
System.out.println("---start---");
new HelloB();
new HelloA();
System.out.println("---end---");
}
static{
System.out.println("----------------------");
}
}
2.内部类
在一个类的内部进行其他类结构的嵌套的操作
- 内部类——局部封装,不让外部类轻易的访问内部类中的属性、方法。内部类可以方便的操作外部类的私有访问
- 内部类使用外部类的属性——外部类的类名.this.属性名
- 内部类使用自身的属性——this.属性名
- 外部类访问内部类的属性必须先创建一个内部类的对象,通过该对象访问内部类的属性
代码示例:
/**
* 内部类与外部类的属性调用
* Author:qqy
*/
public class Outer{
private String msg="this is a attribute";
//Inner:内部类 Outer:外部类
//内部类——局部封装,不让外部类轻易的访问内部类中的属性、方法
class Inner{
private String msg1="this is a inner attribute";
public void print(){
//内部类使用外部类的属性——外部类的类名.this.属性名
System.out.println("Inner中输出的外部类属性;" + Outer.this.msg);
//内部类使用自身的属性——this.属性名
System.out.println("Inner中输出的内部类属性;" + this.msg1);
}
}
public void fun(){
//this指当前对象——Outer,可以调用属性msg
System.out.println(this.msg);
//内部类可以直接访问外部类的属性,但外部类不能直接访问内部类的属性
//System.out.println(Inner.this.msg); //error
//this指当前对象——Outer,而print()是Inner的方法,无法调用
//this.print(); //error
//外部类访问内部类的属性,必须创建一个内部类的对象
//Inner是一个类,有默认的无参构造方法Inner()
//调用print()的正确方法
Inner inner = new Inner();
inner.print();
}
public static void main(String[] args){
Outer outer=new Outer();
outer.fun();
}
}
/**
* 普通类间的调用
* Author:qqy
*/
public class Outer1{
private String msg="this is a attribute";
private Inner inner;
public Outer1(Inner inner){
this.inner=inner;
}
public void fun(){
//Inner2的方法
inner.print();
}
public static void main(String[] args){
Inner inner=new Inner();
Outer1 outer=new Outer1(inner);
outer.fun();
}
}
//普通类
class Inner{
private String msg1="this is a inner attribute";
public void print(){
System.out.println("Inner中输出的普通类属性:" + this.msg1);
}
}
-
2.1 内部类为什么存在
a.内部类与外部类可以方便的访问彼此的私有域(包含私有方法、私有属性)
b.内部类是另一种封装(保护性),可以对同一包中的其他类进行隐藏
c.内部类可以实现Java 单继承的局限
d.想要定义一个回调函数却不想写大量代码的时候,可以选择使用匿名内部类来实现
代码示例:
/**
* 使用内部类来实现"多继承"
* Author:qqy
*/
public class MultipleInheritance {
public static void main(String[] args) {
Outer outer = new Outer();
System.out.println(outer.getName());
System.out.println(outer.getAge());
}
}
class A {
private String name = "A属性";
//getter
public String getName() {
return this.name;
}
}
class B {
private int age = 20;
public int getAge() {
return this.age;
}
}
class Outer {
//内部类InnerA继承普通类A
class InnerA extends A {
public String name() {
return this.getName(); //this表示当前对象
}
}
//内部类InnerB继承普通类B
class InnerB extends B {
public int age() {
return super.getAge(); //super表示调用父类的方法
}
}
public String getName() {
return new InnerA().name();
}
public int getAge() {
return new InnerB().age();
}
}
-
2.2 内部类与外部类的关系
a.对于非静态内部类,内部类的创建依赖外部类的实例对象,在没有外部类实例之前是无法创建内部类的
b.内部类是一个相对独立的实体,与外部类不是is-a关系,不是外部类的属性什么的
c.内部类可以直接访问外部类的元素(包含私有域),但是外部类不可以直接访问内部类的元素
d.外部类可以通过内部类引用,间接访问内部类元素
代码示例:
public class Test{
public static void main(String[] args){
Outer3 outer=new Outer3();
//非静态内部类的创建依赖外部类的实例对象,在没有外部类实例之前是无法创建内部类的
//new InnerA(); //InnerA()被隐藏 error
//非静态内部类在其他地方使用的时候,创建对象的方式
//InnerA 成员内部类,非静态内部类
//InnerA 对象内部 引用了一个外部类对象的引用
Outer2.InnerA innerA = new Outer2().new InnerA();
//使用的时候是独立的个体
System.out.println(innerA.name());
System.out.println(innerA.getNum());
}
}
class A{
private String name="A属性";
//getter
public String getName(){
return this.name;
}
}
class Outer2{
private int num = 20;
//内部类InnerA继承普通类A
class InnerA extends A{
public String name(){
return this.getName(); //this表示当前对象
}
public int getNum(){
System.out.println("原始值:"+Outer2.this.num);
//Outer2.this.num——内部类使用外部类属性
Outer2.this.num=22;
return Outer2.this.num;
}
}
//InnerA().name()——外部类访问内部类的方法或属性,通过创建内部类的实例化对象或者必须持有内部类的实例化对象的引用
public String getName(){
return new InnerA().name();
}
}
class Outer3 extends A{
}
-
2.3 内部类分类
2.3.1 内部类创建语法
- 在使用内部类的时候创建内部类的对象(在外部类的外部)
创建非静态内部类
外部类.内部类 内部类对象 = new 外部类().new 内部类();
Outer.Inner in = new Outer().new Inner();
创建静态内部类
//静态内部类的创建是不需要依赖于外部类,可以直接创建
外部类.内部类 内部类对象 = new 外部类.内部类();
Outer.Inner in = new Outer.Inner();
- 在外部类的内部使用内部类对象
Inner in = new Inner();
2.3.2 成员内部类(类比成员方法)
- 成员内部类中不能存在任何static的变量和方法,但可以访问外部类的静态域(以成员方法为例,static的变量是类变量,若存在成员方法中,则依赖于对象,与类变量矛盾)
- 成员内部类是依附于外部类的,所以只有先创建了外部类才能够创建内部类
2.3.3静态内部类
使用static修饰的内部类我们称之为静态内部类,类似于普通类(类名不同)
- 静态内部类的创建是不需要依赖于外部类,可以直接创建
- 静态内部类不可以访问任何外部类的非static成员变量和方法,但可以存在自己的成员变量(private)
2.3.4方法内部类(局部内部类)
方法内部类定义在外部类的方法中,与成员内部类相比只是作用域不同,方法内部类只能在该方法中被使用,出了该方法就会失效。
- 方法内部类不允许使用访问权限修饰符 public private protected 均不允许
- 方法内部类对外完全隐藏,除了创建这个类的方法可以访问它,其他的地方均不能访问
- 方法内部类要想使用方法形参,该形参必须用final声明(JDK8将形参变为隐式final声明,JDK8之前若没有加final无法通过编译)
代码示例:
/**
* 方法内部类
* Author:qqy
*/
class Outer3 {
private int num;
//相当于public void display(final int test) test已经不可修改
//在一个方法的内部类中访问了参数、变量,
//这时不管是否给变量添加final修饰符,编译器会自动添加
//此后的test无法修改
public void display(int test) {
//方法内部类不允许使用访问权限修饰符
class Inner {
private void fun() {
num++;
System.out.println(num);
System.out.println(test);
}
}
//方法内部类对外完全隐藏,除了创建这个类的方法可以访问它
new Inner().fun();
}
}
public class Test {
public static void main(String[] args) {
Outer3 out = new Outer3();
out.display(20);
}
}
2.3.4匿名内部类(lamdba表达式前身)
是一个没有名字的方法内部类,所以它符合方法内部类的所有约束。
- 匿名内部类是没有访问修饰符的。
- 匿名内部类必须继承一个抽象类或者实现一个接口
- 匿名内部类中不能存在任何静态成员或方法
- 匿名内部类是没有构造方法的(因为它没有类名)
- 与方法内部类相同,匿名内部类也可以引用方法形参。此形参也必须声明为 final
代码示例:
public class Test4 {
public static void main(String[] args) {
Outer outer = new Outer();
outer.display(15);
}
}
interface MyInterFace {
void test(); //public abstract void test(); 抽象方法
}
class Outer {
private int num = 20;
//final int value
public void display(int value) {
//匿名内部类 实现了一个接口MyInterFace
//匿名类不能创建更多的对象,只能有一个(匿名对象、有名字的对象)
new MyInterFace() {
public void test() {
//来自Outer的成员方法的参数value
System.out.println("Value = " + value);
//来自Outer的属性
System.out.println("num = " + num); //20
}
}.test();
}
}