Java复习(3)包、类、内部类、构造方法

本文深入讲解Java的基础概念,包括包和类的定义与作用,构造方法的使用,以及内部类的四种类型:成员内部类、静态内部类、局部内部类和匿名内部类。通过实例演示了如何访问和使用这些内部类。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们在使用eclipse创建java项目的时候会发现,我们得先创建一个包,然后创建一个类才可以写代码,那这些都是什么作用呢?

1. 包

包(package)其实就是文件夹,它的作用是将字节码文件(.class)进行分类存放,包的命名规则:域名倒着写,比如我创建一个项目关于学校人员的增删改查,我可以这么命名:

按功能分按模块分
在这里插入图片描述在这里插入图片描述

包的作用

  1. 把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。

  2. 如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。

  3. 包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。

2. 类

类是什么,如果说包是文件夹,那么类就是文件,我们一直说Java是面对对象的语言,在JAVA语言中我们经常会创建对象,类就是对象的模板,它描述一类对象的行为和状态。

类的内部成员

  • 属性

  • 方法

  • 构造方法(特殊的方法)

  • 程序块:每一次调用构造方法之前,系统会帮我们自动的调用一次程序块,让他执行一次。

特殊的类----内部类

在 Java 中,可以将一个类定义在另一个类或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类

内部类的访问特点:

  • 1.内部类可以直接访问外部类的成员,包括私有
  • 2.外部类要访问内部类的成员,必须创建对象
  • 3.成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。

1.成员内部类

成员内部类是最普通的内部类,它的定义为位于另一个类的内部,形如下面的形式。

class Outer{    
    class Inner{ //内部类        
    }
}

如何访问内部类?看下面的代码演示

public class test {

	public static void main(String[] args) {
		Outer o = new Outer();
		o.outerMethod();
		
		Outer.Inner i = new Outer().new Inner();  //成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。
		i.innerMethod();

	}

}

class Outer{
	private int outerNum = 10;
	
	public void outerMethod(){
		System.out.println(outerNum);
		System.out.println(new Inner().innerNum);  //外部类要访问内部类的成员,必须创建对象
	}
	
	
	class Inner{
		private int innerNum = 20;
		
		public void innerMethod(){
			System.out.println(outerNum); //内部类可以直接访问外部类的成员,包括私有
			System.out.println(innerNum);
		}
	}
}

2.静态内部类

静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。

public class test {

	public static void main(String[] args) {
		Outer.Inner i = new Outer().new Inner();  //成员内部类是依附外部类而存在的
		i.innerMethod();
		
		Outer.Inner1 i1 = new Outer.Inner1();   //静态内部类不依附外部类而存在
		i1.inner1Method();

	}

}

class Outer{
		
	class Inner{		//	成员内部类	
		public void innerMethod(){
			System.out.println("Hello world");
		}
	}
	
	static class Inner1{			//	静态内部类
		public void inner1Method(){
			System.out.println("Hello static");
		}
	}
}

3.局部内部类

局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内

public class test {
	public static void main(String[] args) {
		Outer i = new Outer();
		i.method();
	}
}

class Outer{

	public void method(){
    
		class Inner{   //局部内部类		
			public void innerMethod(){
				System.out.println("Hello world");
			}
		}
		
		//局部内部类,只能在其所在的方法中访问
        Inner i = new Inner();
        i.innerMethod();
	}
}
  • 局部内部类在访问他所在方法中的局部变量必须用final修饰,为什么?
    • 因为当调用这个方法时,局部变量如果没有用final修饰,他的生命周期和方法的生命周期是一样的,当方法弹栈,这个局部变量也会消失,这时如果局部内部类对象还没有消失,那么他就在继续享用这个已经不存在的局部变量;但是如果用final修饰这个局部变量,他就会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也可以继续使用 ,但是JDK1.8取消了这个事情。

4.匿名内部类

匿名内部类:内部类的简化写法,它的格式是:

new 类名或者接口名(){
    重写方法;
}

匿名内部类使用的前提是,这个类是存在的类或者接口, 这里的类可以是具体类也可以是抽象类
匿名内部类本质是一个继承了该类或者实现了该接口的子类匿名对象

//  测试类`
public class test {

	public static void main(String[] args) {
		Outer o = new Outer();
		o.Method();
		
		Outer.Inner i = new Outer().new Inner();
		i.print();
	}

}

// 已经存在的接口
interface Inter{
    public void print();
}


class Outer{
    // 新建一个类继承接口并重写方法
    class Inner implements Inter{
        public void print() {
            System.out.println("print");
    }
}
    public void Method() {
       //实现了Inter接口的子类匿名对象
        new Inter() {
            public void print() {
                System.out.println("print");//重写抽象方法
            }
        }.print();   //.print()前面整个代表Inter的子类的对象,
    }
}

匿名内部类重写 多个方法 调用

public class test {
    public static void main(String[] args) {
        Outer o = new Outer();
        o.Method();
    }
}


interface Inter{
    public void print1();
    public void print2();
}


class Outer{

    public void Method() {

        new Inter() {
            public void print1() {
                 System.out.println("print1");
             }


            public void print2() {
                System.out.println("print2");
            }
        }.print2();


        //或者
        Inter i = new Inter() {
            public void print1() {
                System.out.println("print1");
            }


            public void print2() {
                System.out.println("print2");
            }
        };
        i.print2();
    }
}
匿名内部类在开发中的利用

举例:看下面的代码,问如何调用PersonDemo中的method方法呢?

public class  test {
    public static void main(String[] args) {        
        PersonDemo pd = new PersonDemo();
        pd.method(new Person());//错误,因为Person类不可实例化
    }
}

abstract class Person{
    public abstract void show();
}


class PersonDemo{
    public void method(Person p) {
        p.show();
    }
}

解决方法一:

public class test {
    public static void main(String[] args) {
         PersonDemo pd = new PersonDemo();        
        pd.method(new Student()); 
    }
}

abstract class Person{
    public abstract void show();
}


class PersonDemo{
    public void method(Person p) {
        p.show();
    }
}


class Student extends Person{
    public void show() {
        System.out.println("print");
    }
}

解决方法二:

public class test {
    public static void main(String[] args) {
        PersonDemo pd = new PersonDemo();
        //匿名内部类当做参数传递(本质把匿名内部类看做一个对象)
        pd.method(new Person() {
            public void show() {
                System.out.println("print2");
            }
        });
    }
}

abstract class Person{
    public abstract void show();
}


class PersonDemo{
   public void method(Person p) {
        p.show();
    }
}

内部类的面试题

  1. 成员内部类的面试题:对于下面的代码,使用已知的变量,在控制台输出30,20,10
public class test {
    public static void main(String[] args) {
        Outer.Inner oi = new Outer().new Inner();
        oi.show();
    }
}

class Outer{
    public int num = 10;

    class Inner{
        public int num = 20;

        public void show() {
            int num = 30;
            System.out.println(?);
            System.out.println(?);
            System.out.println(?);
        }
    }
}

答案:

public class test {
    public static void main(String[] args) {
        Outer.Inner oi = new Outer().new Inner();
        oi.show();
    }
}

class Outer{
    public int num = 10;
    
    class Inner{
        public int num = 20;


        public void show() {
            int num = 30;
            System.out.println(num);
            System.out.println(this.num);
            System.out.println(Outer.this.num);//内部类之所以能获取到外部类的成员,是因为它能获取到外部类的引用
        }
    }
}

匿名内部类面试题:按照要求,补齐代码,要求在控制台输出“HelloWorld"

public class test {
    public static void main(String[] args) {
        Outer.method().show();
    }
}

interface Inter{
    void show();
}

class Outer{
    //补齐代码
}

答案:

public class test {
    public static void main(String[] args) {
        //链式编程,每次调用方法后还能继续调用方法,说明调用方法返回的是一个对象
        Outer.method().show();
    }
}

interface Inter{
    void show();
}

class Outer{
    public static Inter method(){
        return new Inter() {
            public void show() {
                System.out.println("HelloWorld");
            }
        };
    }
}

3. 方法

什么是方法:完成特定功能的代码块

我们在写代码的时候不可能一致把所有的代码都写在main方法里,为了提高代码的复用性,我们会将完成特定功能的代码放进一个代码块里,这个代码块就称为方法。方法的格式如下:

修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2...) {
	方法体语句;
	return 返回值; 
} 

4. 构造方法

在类中,有一个特殊的方法称为 构造方法,每个类都有构造方法。如果没有显式地为类定义构造方法,Java编译器将会为该类提供一个默认构造方法。

在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,一个类可以有多个构造方法。

  1. 作用:构建当前类的对象,给对象的数据(属性)进行初始化
  2. 格式:
    a:方法名与类名相同(大小也要与类名一致)
    b:没有返回值类型,连void都没有
    c:没有具体的返回值return
    在这里插入图片描述
  3. 构造方法不能用对象调用
public class test {

    public static void main(String[] args) {
        Person p = new Person();  //输出 This is a person(在一创建对象的时候,系统就帮我们调用了构造方法)
        //p.Person(); 构造方法不能用对象调用
    }
}


class Person{
    private String name;
    private int age;

    //构造方法
    public Person() {
        System.out.println("This is a person");
    }
}

构造方法的重载

在写构造方法的重载时,先写一下什么是重载

重载overload

  • 什么是重载:方法名相同,参数列表不同,与返回值无关,只看参数列表。参数的不同体现在:参数的个数、参数的类型、参数的顺序三个方面
  • 重载的作用:使用起来更加方便。
  • 注意:
    JDK1.5 之后 重载新的写法:动态参数列表...,即参数类型固定,参数的个数可以动态设定0~n个。使用起来类似于数组的使用,可以有length 、index,如下图:

    注意:参数为动态参数列表的方法 与 对应参数为匹配的数组类型的方法 不能构成重载。因为两个本质是一样的。如下图就会报错。
    在这里插入图片描述
    但是两者还是有区别的:
  1. 动态参数列表方法使用时可以不传递参数
  2. 数组方法必须传递形参
  3. 动态参数列表在方法参数中只能存在一份,且放置在参数列表最后,因为不知道什么时候是个头。

构造方法重载后的注意事项:

  1. 如果你没有给出构造方法,则系统会默认给你空参数的构造方法。
  2. 如果给出了构造方法(不管有参数没有),系统提供的默认的构造方法就会不再提供,建议在重载有参数的构造方法时,就加上没有参数的构造方法(即使没用),以防出错。
案例一–一个完整的学生类

建立一个完整的学生类,为了防止被任意修改,在类中一般属性是私有的,但是为了还是能访问到属性,会给出set和get方法。

class Student{
    private String name;//私有属性
    private int age;

    // 空参构造
    public Student() { }
	//  构造方法的重载
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }


    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }


    public void setAge(int age) {
        this.age = age;
    }
    public int getAge() {
     return age;
    }
    
    public void show() {
        System.out.println(name+"...."+age);
    }
}
案例二

需求:

  • 1.定义一个长方形,定义 求周长和面积的方法
  • 2.定义一个测试类进行测试

分析:

  • 成员变量:宽和高
  • 成员方法:getLength();getArea();
class Rectangle{
    private int width;
    private int high;


    private Rectangle() {    }
    
    
    public Rectangle(int width,int high) {
        this.width = width;
        this.high = high;
    }


    public void setWidth(int width) {
        this.width = width;
    }
    public int getWidth() {
        return width;
    }
    public void setHigh(int high) {
        this.high = high;
    }
    public int getHigh() {
        return high;
    }


    public int getLength() {
        return 2*(width+high);
    }


    public int getArea() {
        return (width*high);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值