类是抽象的,概念的;对象是具体的,实际的;类说对象的模板,对象是类的一个个体
属性
属性的定义语法同变量:访问修饰符 属性类型 属性名
属性的定义类型可以是任意类型,包含基本类型或引用类型
属性如果不赋值,有默认值,规则和数组一样
创建对象方法
先声明再创建
Cat cat;
cat = new Cat()
直接创建
Cat cat = new Cat()
Java内存分析
栈:一般存放基本数据类型(局部变量)
堆:存放对象
方法区:常量池(常量,字符串),类加载信息
方法重载
java中允许同一个类中,多个同名方法存在,但要求形参列表不一致
方法名:必须相同
形参列表:必须不同(参数类型或个数或顺序,至少有一样不同,参数名无要求)
返回类型:无要求
可变参数
java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法
语法:访问修饰符 返回类型 方法名(数据类型... 形参名){ }
【基本使用】
//1. int... 表示接收的是可变参数,类型为int,即可以接收多个int(0个或多个)
//2.使用可变参数时,可以当作数组来使用 即nums可以当作数组
public int sum(int... nums){
System.out.println(nums.length);
return 0;
}
可变参数的实参可以为0个或任意多个
可变参数的实参可以为数组
可变参数的本质就是数组
可变参数可以和普通类型的参数一起放在形参列表,但必须爆炸可变参数在最后
一个形参列表中只能出现一个可变参数
构造方法/构造器
构造方法又叫构造器,是类的一种特殊的方法,它的主要作用就是完成对新对象的初始化
语法:[修饰符] 方法名(形参列表){
方法体
}
构造器的修饰符可以默认
构造器没有返回值
方法名和类名必须相同
构造器的调用由系统完成
一个类可以定义多个构造器,即构造器重载
构造器是完成对象的初始化,并不是创建对象
在创建对象时,系统自动调用该类的构造方法
如果程序员没有定义构造方法,系统会自动给类生成一个默认无参构造方法
一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义无参构造器
this
java虚拟机给每个对象分配this,代表当前对象。
简单来说,哪个对象调用,this就代表哪个对象
this关键字可以用来访问本类的属性,方法,构造器
this用于区分当前类的属性和局部变量
访问成员方法的语法:this.方法名(参数列表)
访问构造器语法: this(参数列表):注意只能再构造器中使用
this不能在类定义的外部使用,只能在类定义的方法中使用
访问修饰符
java提供四种访问修饰符,用于控制方法和属性(成员变量)的访问权限:
public
公开级别,对外公开
protected
对子类和同一个包中的类公开
默认级别
没有修饰符,向同一个包的类公开
private
只有本类可以访问,不对外公开
修饰符可以同来修饰类中的属性,成员方法和类
只有默认和public可以修饰类
封装
封装就是把抽象出来的数据【属性】和对谁家的操作【方法】封装在一起,数据被保护在内部,程序的其它部分只有通过授权的操作【方法】才能对数据进行操作
封装的实现步骤
将属性进行私有化
提供一个公共的set和get方法
继承
继承可以解决代码复用,让我们的编程更加接近人类思维,当多个类存在相同的属性和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。
继承的基本语法
class 子类 extends 父类{
}
子类会自动拥有父类定义的属性和方法
注意细节:
1,子类继承了所有的属性和方法,但私有属性不能在子类中直接访问,要通过公共的方法取访问
2,子类必须调用父类的构造器,完成父类的初始化
3,当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中使用supper去指定父类的哪个构造器完成对父类的初始化工作,否则,编译不通过
4,如果希望指定去调用父类的某个构造器,则显示的调用一下
5,super在使用时,需要放在构造器第一行(super只能在构造器中使用)
6,super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
7,java所有类都是Object的子类,Object是所有类的基类
8,父类构造器的调用不限于直接父类,将一直往上追溯到Object类(顶级父类)
9,子类最多只能继承一个父类,即java的单继承机制
10,不能滥用继承,子类和父类之间必须满足is-a的逻辑关系
11,如果子类和父类有相 同的属性时,访问子类这个属性,会优先查看子类这个属性是否可以访问,如果可以,则返回子类的这个属性,否则,就去访问父类的这个属性。
super
super代表父类的引用,用于访问父类的属性,方法,构造器
访问父类的属性和方法,但不访问私有的
访问父类的构造器:super(参数列表);参数列表对于不同的构造器,只能放在子类构造器的第一句,只能出现一句
super给编程带来的便利
1,调用父类构造器的好处:分工明确,父类属性由父类初始化,子类属性由子类初始化
2,当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。用法:super.方法名()
3,super的访问不限于直接父类,如果父类的父类和本类有同名的成员,也可以使用super去访问父类的父类的成员;如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。(遵守访问权限规则)
方法重写/覆盖
方法的重写就是子类有一个方法,和父类的某个方法的名称,返回值,参数一样,那么我们就说这个子类的这个方法重写/覆盖了父类的方法
细节
1,子类方法的返回类型和父类方法的返回类型一样,或者是父类返回类型的子类。
2,子类方法不能缩小父类方法的访问权限
多态
基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承的基础之上的
对象的多态(核心)
一个对象的编译类型和运行类型不一致
编译类型在定义对象时,就确定了,不能改变
运行类型是可以变化的
编译类型看定义时“=”的左边,运行类型看 “=”的右边
多态的向上转型
本质:父类的引用指向了子类的对象
语法:父类类型 引用名 = new 子类类型();
特点:编译类型看左边,运行类型看右边;可以调用父类的所有成员(须遵守访问权限),不能调用子类中 的特有成员;最终运行效果看子类的具体表现
【测试环境】
package java_fundation.super_;
public class Animal {
String name = "动物";
int age = 18;
public void sleep(){
System.out.println("睡");
}
public void eat(){
System.out.println("吃");
}
public void run(){
System.out.println("跑");
}
public void show(){
System.out.println("hello,你好");
}
}
package java_fundation.super_;
public class Cat extends Animal {
public void eat(){
System.out.println("猫吃鱼");
}
public void catchMouser(){
System.out.println("猫抓老鼠");
}
}
【测试类】
package java_fundation.super_;
public class Client {
public static void main(String[] args) {
//向上转型
Animal animal = new Cat();
//也符合语法
Object object = new Cat();
//不能调用子类特有的成员
//animal.catchMouser();
/*
在编译阶段,能调用哪些成员,是由编译类型决定的
最终运行结构看子类的具体表现,即调用方法时,按照从子类开始查找方法,然后调用
*/
animal.eat();//猫吃鱼
animal.run();//跑
}
}
多态的向下转型
语法 :子类类型 引用名 = (子类类型)父类引用;
只能强转父类的引用,不能强转父类的对象
要求父类的引用必须指向的是当前目标类型的对象
可以调用子类类型中所有的成员
【测试环境】
新增Dog类
package java_fundation.super_; public class Dog extends Animal{ }
【测试类】
package java_fundation.super_;
public class Client {
public static void main(String[] args) {
Animal animal = new Cat();
//向下转型
Cat cat = (Cat)animal;
cat.catchMouser();
//要求父类的引用必须指向的是当前目标类型的对象
Dog dag = (Dog)animal;//报错
}
}
属性重写问题
属性没有重写之说,属性的值看编译类型
instanceof比较操作符
用于判断对象的运行类型是否为XX类型或XX类型的子类型
【测试类】
package java_fundation.super_;
public class Client {
public static void main(String[] args) {
Cat cat = new Cat();
System.out.println(cat instanceof Cat);//true
System.out.println(cat instanceof Animal);//false
Animal animal = new Cat();
System.out.println(animal instanceof Cat);//true
System.out.println(animal instanceof Animal);//true
}
}
动态绑定机制【重要】
1,当调用对象方法时,该方法会和该对象的内存地址/运行类型绑定
2,当调用对象属性时,没有动态绑定属性,哪里声明,哪里使用
【测试环境】
package java_fundation.poly_dynamic;
public class A {
public int i;
public int sum(){
return getI()+10;
}
public int sum1(){
return i+10;
}
public int getI(){
return i;
}
}
package java_fundation.poly_dynamic;
public class B extends A{
public int i =20;
public int sum(){
return i+20;
}
public int getI(){
return i;
}
public int sum1(){
return i + 10;
}
}
【测试类】
package java_fundation.poly_dynamic;
public class Test {
public static void main(String[] args) {
A a = new B();
System.out.println(a.sum());//调用B类的方法和属性,结果为40
System.out.println(a.sum1());//调用B类的方法和属性,就跟为30
}
}
当注释掉B类的sum和sum1方法后
package java_fundation.poly_dynamic;
public class B extends A{
public int i =20;
// public int sum(){
// return i+20;
// }
public int getI(){
return i;
}
// public int sum1(){
// return i + 10;
// }
}
测试类结果
package java_fundation.poly_dynamic;
public class Test {
public static void main(String[] args) {
A a = new B();
System.out.println(a.sum());//调用A类的sum方法,然后调用B类的getI方法,结果为30
System.out.println(a.sum1());//调用A类的sum1方法,结果为20
}
}
多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
多态参数
方法定义的形参类型为父类类型,实参类型为子类类型
Object类详解
类object是类层次结构的根类。每个类都使用object作为超类。所有对象(包括数组)都实现这个类的方法
equlas方法
“==”和equlas的对比
1,==:既可以判断基本类型,又可以判读引用类型
2,==:如果判断基本类型,判断的是值是否相等。
3,==:如果判断的是引用类型,判断的是地址是否相等,即判断是不是同一个对象
4,equlas:是Object类中的方法,只能判断引用类型
5,默认判断的是地址是否相等,子类往往可以重写该方法,用于判断内容是否相等。比如Integer,String。
自定义重写equlas方法
判断对象的属性是否相同
package java_fundation.obiect_; public class EqualsExercise01 { public static void main(String[] args) { Person person1 = new Person("jack", 10, '男'); Person person2 = new Person("jack", 10, '男'); Person person3 = new Person("Tom", 11, '女'); System.out.println(person1.equals(person2));//true System.out.println(person1.equals(person3));//false Person person4 = person1; System.out.println(person1.equals(person4));//true } } class Person { private String name; private int age; private char gender; public Person(String name, int age, char gender) { this.name = name; this.age = age; this.gender = gender; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public char getGender() { return gender; } public void setGender(char gender) { this.gender = gender; } //重写object的equals方法 public boolean equals(Object obj) { //判断如果比较的两个对象是同一个对象,则直接返回true if(this == obj){ return true; } //类型判断 if(obj instanceof Person){ //类型转化 Person person = (Person)obj; return this.name.equals(person.getName()) && this.age == person.getAge() && this.gender == person.getGender(); } return false; } }
hashCode方法
返回该对象的哈希码值,支持此方法是为了提高哈希表的性能
1,提高哈希结构的容器的效率
2,两个引用,如果指向的是同一个对象,则哈希值肯定一样
3,两个引用,如果指向的是不同对象,则哈希值不一样
4,哈希值主要是根据地址号来的,不能将哈希值等价于地址
toString方法
基本介绍
默认返回:全类名+@+哈希值的十六进制(jdk源码)
子类往往重写toString方法,用于返回对象的属性信息
如对以上的Person类重写toString方法
@Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", gender=" + gender + '}'; }
当我们直接输出一个对象时,toString方法会被默认的调用
finalize方法
在实际开发中,几乎不会去运用
1,当对象被回收,系统自动调用该对象的finalize方法。子类可以重写给方法,做一些释放资源的操作
2,什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁对象,在销毁该对象,会先调用finalize方法。
3,垃圾回收机制的调用,是由系统决定,也可以通过System.gc()主动触发垃圾回收机制。
package java_fundation.obiect_; public class Finalize_ { public static void main(String[] args) { Car car = new Car("宝马"); car = null;//这时对象空间就是一个垃圾,垃圾回收器就会回收(销毁)对象,在销毁对象前,会调用该对象的finalize方法 //可以在这个方法中写入自己的业务逻辑代码,比如释放资源,数据库的连接,或者打开文件 //如果不重写finalize方法,就会调用Object类的finalize方法,即默认处理 //主动调用垃圾回收器 System.gc(); System.out.println("程序退出"); } } class Car{ private String name; public Car(String name){ this.name = name; } //重写finalize方法 @Override protected void finalize() throws Throwable { System.out.println("销毁汽车"+name); } }
静态变量(类变量)
static变量是同一个类所有对象共享的;
static变量在类加载的时候就生成了;
什么是类变量
类变量也就静态变量/静态属性,是该类的所有对象共享的变量,是任何一个该类的对象去访问它时,取得的都是相同的值,统统该类的对象去修改它时,修改的也是同一个变量
如何定义类变量
定义语句:
访问修饰符 static 数据类型 变量名
如何访问类变量
类名.类变量名
对象.类变量名
类方法
基本介绍
类方法也就静态方法
访问修饰符 static 数据返回类型 方法名(){ }
在类加载时就生成了
类方法的调用
使用方法:类名.类方法名或者 对象.类方法名
注意点
1,类方法中不允许是有和对象有关的关键字,比如this和super
2,类方法(静态方法)中只能访问静态变量或静态方法
3,普通成员方法,可以访问静态成员和非静态成员
main语法说明
1,java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public
2,java虚拟机在执行main()方法时不必创建对象,所以该方法必须时static
3,该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数
特别提示
1,在mian()方法中,我们可以直接调用main方法所在类的静态方法或静态属性
2,但是,不能直接调用该类的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员
代码块
代码块又称为初始代码块,属于类中的成员【即是类的一部分】,类似于方法,将逻辑语句封装在方法体中,通过{}包围起来
但和方法不同,没有方法名,没有返回,没有参数,只要方法体,而且不用通过对象或类显示调用,而是在类加载时,或创建对象时隐式调用。
基本语法
[修饰符]{
};
修饰符可选写,要写的话,只能写static
代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的,叫普通代码块
逻辑语句可以为任何逻辑语句
作用
相当于另外一种形式的构造器(对构造器的补充机制),可以初始化操作
如果多个构造器中有重复语句,可以抽取到初始代码块中
代码块调用的顺序优先于构造器
package java_fundation.codeblock_;
public class CodeBlock01 {
public static void main(String[] args) {
Movie movie = new Movie("终结者");
}
}
class Movie{
private String name;
private String price;
private String director;
{
System.out.println("电影开始了");
System.out.println("广告开始播放");
}
public Movie(String name) {
System.out.println("Movie(String name)被调用");
this.name = name;
}
public Movie(String name, String price) {
this.name = name;
this.price = price;
}
public Movie(String name, String price, String director) {
this.name = name;
this.price = price;
this.director = director;
}
}
代码块使用细节
1,static代码块也叫静态代码块,作用是对类进行初始化,而且它随着类的加载而执行,并且只执行一次。如果是普通代码块【没有被static修饰的代码块】,每创建一个对象,就执行一次
2,类什么时候被加载
创建对象实例时(new)
创建子类对象实例时,父类也会被加载
使用类的静态成员时
3,普通代码块,在创建对象实例时,会被隐式的调用。每创建一次,就会调用一次。如果使用类的静态成员,普通代码块并不会执行
4,创建一个对象时,在一个类调用的顺序
①调用静态代码块的静态属性初始化(静态代码和静态属性初始化调用优先级一样,如果有多个静态代码块和多个静态属性变量初始化,则按照他们定义的顺序执行)
②调用普通代码块和普通属性的初始化(普通代码块和普通属性初始化的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按照定义顺序执行)
③调用构造方法
package java_fundation.codeblock_;
public class CodeBlock02 {
public static void main(String[] args) {
A a = new A();
}
}
class A{
private int n2 = getN2();
{
System.out.println("A 普通代码块01");
}
//静态属性
private static int n1 = getN1();
static {
System.out.println("A 静态代码块01");
}
public static int getN1(){
System.out.println("getN1 被调用");
return 100;
}
public int getN2(){
System.out.println("getN2被调用");
return 200;
}
public A() {
System.out.println("A 无参构造器");
}
}
/*
输出结果
getN1 被调用
A 静态代码块01
getN2被调用
A 普通代码块01
A 无参构造器
*/
5,构造器的最前面隐含了super()和调用普通代码块。静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的。
6,创建子类时的调用顺序
①父类的静态代码块和静态属性
②子类的静态代码块和静态属性
③父类的普通代码块和普通属性初始化
④父类的构造方法
⑤子类的普通代码块和普通属性初始化
⑥子类的构造方法
7,静态代码块只能调用静态成员,普通代码块可以调用任意成员
单例模式
所谓单例模式,就是采用一定的方式保证在整个软件系统中,对某个类只能存在一个实例,并且该类只提供一个取得其对象实例的方法
饿汉式
无论是否使用,在类加载时就会创建实例
①构造器私有化
②类的内部静态创建对象
③向外暴露一个静态的公共方法
package java_fundation.single_;
//饿汉式
public class Single01 {
public static void main(String[] args) {
GirlFriend instance = GirlFriend.getInstance();
System.out.println(instance);
GirlFriend instance2 = GirlFriend.getInstance();
System.out.println(instance2 == instance);//true
}
}
class GirlFriend{
private String name;
//在类内部创建对象
private static GirlFriend gf = new GirlFriend("小红");
//构造器私有化
private GirlFriend(String name){
this.name = name;
}
//提供一个公共的static方法,返回gf对象
public static GirlFriend getInstance(){
return gf;
}
@Override
public String toString() {
return "GirlFriend{" +
"name='" + name + '\'' +
'}';
}
}
懒汉式
只有在使用时才会创建这个实例
①构造器私有化
②类的内部创建对象
③向外暴露一个静态的公共方法
package java_fundation.single_;
public class GirlFriend02 {
public static void main(String[] args) {
Cat cat = Cat.getCat();
System.out.println(cat);
Cat cat1 = Cat.getCat();
System.out.println(cat1);
System.out.println(cat1 == cat);//true
}
}
class Cat{
private String name;
private static int age = 18;
//先在类的内部声明
private static Cat cat;//默认为空
//构造器私有化
private Cat(String name){
this.name = name;
System.out.println("构造器被调用");
}
//向外提供一个静态的公共方法
public static Cat getCat(){
if(cat == null){
cat = new Cat("小猫娘");
}
return cat;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
/*
输出结果
构造器被调用
Cat{name='小猫娘'}
Cat{name='小猫娘'}
true
*/
小结
1,二者最主要的区别在于创建对象的时间不同:饿汉式在类加载就创建了对象实例,而懒汉式在使用时才创建
2,饿汉式不存在线程安全问题,懒汉式存在线程安全问题
3,饿汉式存在资源浪费
final关键字
基本结束
final可以修饰类,属性,方法和局部变量
1,被final修饰的类,不可以被继承
2,被final修饰的方法,不可以被重写/覆盖
3,被final修饰的属性,不可以被修改
使用细节
1,final修饰的属性又叫床两,一般用 xx_xx_xx来命名
2,final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一
①定义时
②构造器中
③代码块中
3,如果final修饰的属性是静态的,则初始化的位置只能是
①定义时
②在静态代码块中,不能在构造器中赋值
4,final修饰不能继承,但是可以实例化对象
5,如果类不是final,但是含有final方法,则该方法不能被重写,但是可以被继承
5,一般来说,如果一个类已经是final类了,就没必要再将方法修饰为final方法
6,final不能修饰构造器
7,final和static往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理
8,包装类(Integer,Doubl,Float,Boolean等都是final)String也是final类
transient关键字
总的来说:transient
修饰的关键字不会被序列化
-
一旦变量被
transient
修饰,变量不再是对象持久化的一部分,该变量在反序列化后也无法获得 -
transient
关键字只能修饰变量,不能修饰方法和类 -
一个静态变量不管是否被
transient
修饰,均不能被序列化
抽象类(abstract)
用abstract关键字修饰的类叫做抽象类
用abstract关键字修饰的方法叫做抽象方法,抽象方法不能有方法体
当一个类中存在抽象方法时,需要将带类声明为abstract
一般来说,抽象类会被继承,由它的子类来实现抽象方法
使用细节
1,抽象类不能被实例化
2,一旦类包含了abstract方法,则这个类必须声明为anstract类
3,抽象类不一定要包含抽象方法,也就是说,抽象类可以没有abstract方法。
4,abstract只能修饰类和方法。
5,抽象类可以有任何成员,包括构造器,静态属性等
6,抽象方法不能有方法体
7,如果一个类继承了抽象类,则它必须实现抽象类的所有方法,除非它自己也声明为abstract类
8,抽象方法不能使用private,final和static来修饰,因为这些关键字都是和重写相违背的
接口(interface)
基本介绍
接口就是给出一些没有实现方法,封装到一起,到某个类要使用时,在根据具体情况把这些方法实现
在jdk7.0前,接口里所有方法都没有方法体,即都是抽象方法。
在jdk8.0后,接口可以有静态方法,默认方法(必须使用default关键字修饰),也就是说接口可以有方法的具体实现
使用细节
1,接口不能被实例化
2,接口中的所有方法是public方法,接口中的抽象方法,可以没有abstract修饰
3,一个普通类实现接口,就必须将该接口中的所有方法都实现
4,抽象类实现接口,可以不用实现接口的方法
5,一个类可以同时实现多个接口
6,接口中的属性,只能是final的,而且是public static final 修饰符,必须初始化,比如int a = 1;实际上是public static final final int a = 1;
7,接口中的属性访问形式:接口名.属性名
8,一个接口不能继承其它类,但是可以继承多个别的接口
9,接口的修饰符,只能是public和默认
接口和继承
接口和继承解决的问题不同
继承的价值在于:解决代码复用性和可维护性
接口的价值在于:设计,设计好各种规范(方法),让其它类去实现这些方法
接口比继承更加灵活
接口时满足is-a的关系,继承是只需要满足like-a的关系
接口在一定程度上实现代码解耦
接口多态特性
接口类型的变量可以指向实现了接口类的对象实例
多态参数
多态数组
接口的多态传递
package java_fundation.interface_;
public class InterfacePoly {
public static void main(String[] args) {
Interface2 interface2 = new Test();
//如果Interface2继承了interface1接口,而Test类实现了Interface2接口
//就相当于Test类也实现了Interface1接口
Interface1 interface1 = new Test();
}
}
interface Interface1 {
void hi();
}
interface Interface2 extends Interface1 {
void hello();
}
class Test implements Interface2 {
@Override
public void hello() {
}
@Override
public void hi() {
}
}
内部类
基本介绍
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类,嵌套其它类的类称为外部类。是我们类的第五大成员【属性,方法,构造器,代码块,内部类】,内部类最大的特点是可以之间访问私有属性,并且可以体现类与类之间的包含关系。
基本语法
class Outer{//外部类
class Innter{//内部类
}
}
内部类的分类
定义在外部类局部位置上(比如方法内):
局部内部类(有类名)
匿名内部类(没有类名)
定义在外部类的成员位置上:
成员内部类(没用static修饰)
静态内部类(使用static修饰)
局部内部类
说明
局部内部类是定义在外部类的局部位置上,比如方法或代码块中,并且有类名,本质任然是一个类
使用细节
1,可以直接访问外部类的所有成员,包括私有的
2,不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量可以使用final修饰。
3,作用域:仅仅在定义它的方法或代码块中
4,局部内部类访问外部类的成员
访问方式:直接访问
5,外部类访问局部内部类的成员
访问方式:创建对象,再访问(注意:必须在作用域内)
6,如果外部类和局部内部类的成员重名,默认就近原则,如果想访问外部类的成员,则可以使用(外部类.this.成员)
外部类.this本质就是外部类的对象,即哪个对象调用了方法或属性,外部类.this就是哪个对象
7,外部其他类不能访问局部内部类(因为局部内部类是一个局部变量)
【测试】
package java_fundation.innerclass;
public class LocalInnerClass {
public static void main(String[] args) {
Outer outer = new Outer();
outer.m1();
}
}
class Outer{//外部类
private int n1 = 100;
private void m2(){}
public void m1(){
//局部内部类是定义在外部类的局部位置上,通常是在方法
class Inner{//局部内部类(本质任然是一个类)
private int n1 = 800;
public void f1(){
//可以直接访问外部类的所有成员,包括私有的
//遵守就近原则,调用Inner局部内部类的n1
System.out.println("n1=" + n1);
//访问外部类成员(和内部类重名的成员)
System.out.println("外部类的n1=" + Outer.this.n1);
m2();
}
}
//外部类访问局部内部类的成员
Inner inner = new Inner();
inner.f1();
}
}
匿名内部类【重要】
说明
匿名内部类是定义在外部类的局部位置上,比如方法,并且没有类名,本质还是类,同时还是一个对象
基本语法
new 类或接口(参数列表){
类体
}
【测试】
package java_fundation.innerclass;
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.method();
}
}
class Outer02{
private int n1 = 10;
public void method(){
//编译类型是A
//运行类型就是匿名内部类
/*
底层会分配类名 Outer02$1
class Outer02$1 implements A{
@Override
public void cry() {
System.out.println("老虎在叫");
}
}
*/
//jdk底层在创建匿名内部类Outer02$1,立即马上就创建了Outer02$1的实例,并且把地址返回给tiger
A tiger = new A(){
@Override
public void cry() {
System.out.println("老虎在叫");
}
};
tiger.cry();
System.out.println("tiger的运行类型=" + tiger.getClass());
//基于类的匿名内部类
//编译类型 Father
//运行类型 Outer02$2
Father father = new Father("jack"){
@Override
public void test(){
System.out.println("匿名内部类重写test方法");
}
};
System.out.println("father对象的运行类型" + father.getClass());
//抽象类的匿名内部类
Animal animal = new Animal(){
@Override
void eat() {
System.out.println("小马在吃草");
}
};
animal.eat();
}
}
interface A{
public void cry();
}
class Father{
public Father(String name){
}
public void test(){
}
}
abstract class Animal{
abstract void eat();
}
/*
输出结果
老虎在叫
tiger的运行类型=class java_fundation.innerclass.Outer02$1
father对象的运行类型class java_fundation.innerclass.Outer02$2
小马在吃草
*/
使用细节
1,匿名内部类的语法比较,匿名内部类即是一个类的定义,也是一个一个对象,因此从语法上,它既有定义类的特征,也有创建类对象的特征。
2,可以直接访问外部类的所有成员,包括私有的
3,不能添加访问修饰符,因为它是地位就是一个局部变量
4,作用域:仅仅在定义它的方法或代码块中
5,匿名内部类访问外部类成员:访问方式,直接访问
6,外部其它类不能访问匿名内部类
7,如果外部类和内部类的成员重名时,匿名内部类访问的话,默认就近原则,如果想访问外部类的成员,则可以使用(外部类.this.成员)访问
package java_fundation.innerclass;
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
Outer03 outer03 = new Outer03();
outer03.f1();
System.out.println("main Outer03 hashcode" + outer03);
}
}
class Outer03{
private int x = 60;
public void f1(){
Person p = new Person(){
//和外部类成员重名
private int x = 99;
@Override
public void hi(){
System.out.println("匿名内部类重写hi方法");
System.out.println("访问匿名内部类的x=" + x);
System.out.println("访问外部类的x=" + Outer03.this.x);
System.out.println("Outer03.this hashcode" + Outer03.this);
}
};
p.hi();//动态绑定,运行类型是Outer03
//直接调用,匿名内部类直接返回的是一个对象·
new Person(){
@Override
public void hi(){
System.out.println("直接调用方法");
}
}.hi();
}
}
class Person{
public void hi(){
System.out.println("Person hi()");
}
}
/*
输出结果
匿名内部类重写hi方法
访问匿名内部类的x=99
访问外部类的x=60
Outer03.this hashcodejava_fundation.innerclass.Outer03@d716361
直接调用方法
main Outer03 hashcodejava_fundation.innerclass.Outer03@d716361
*/
成员内部类
说明
成员内部类是定义在外部类的成员位置,并且没有static修饰
实现细节
1,可以直接访问外部类的所有成员
2,可以添加任意访问修饰符
3,作用域和其它外部类的成员一样,为整个类体
4,成员内部类访问外部类成员:直接访问
5,外部类访问成员内部类:创建对象,再访问
6,外部其它类访问成员内部类:两种方法
7,如果外部类和内部类的成员重名时,成员内部类访问的话,默认就近原则,如果想访问外部类的成员,则可以使用(外部类.this.成员)访问
【测试】
package java_fundation.innerclass;
public class MemberInnerClass {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.test();
//外部类访问成员内部类的两种种方式
//第一种
Outer04.Inner04 inner04 = outer04.new Inner04();
//第二种 在外部类中,编写一个方法,返回Inner04内部类对象
Outer04.Inner04 inner041 = outer04.getInner04();
}
}
class Outer04{
private int n1 = 10;
public String name = "jake";
class Inner04{//成员内部类
public void say(){
System.out.println("n1 = " + n1 + "name=" + name);
}
}
public void test(){
//使用内部类
Inner04 inner04 = new Inner04();
inner04.say();
}
//返回内部类的实例
public Inner04 getInner04(){
return new Inner04();
}
}
静态内部类
说明
静态内部类是定义在外部类的成员位置,并且有static修饰
使用细节
1,可以直接访问外部类的所有静态成员,包括私有的,但不能访问非静态成员
2,可以添加任意访问修饰符,因为它的地位就是一个类成员
3,作用域:同其他的成员一样,是整个类体
4,静态内部类访问外部类(静态成员):直接访问
5,外部类访问静态内部类:创建对象,再访问
6,外部其他类访问静态内部类:两种方式
7,如果外部类和内部类的成员重名时,静态内部类访问的话,默认就近原则,如果想访问外部类的成员,则可以使用(外部类.this.成员)访问
p
ackage java_fundation.innerclass;
public class StaticInnerClass {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.show();
//外部其他类访问静态内部类
//方式一
Outer05.Inner05 inner05 = new Outer05.Inner05();
//第二种 在外部类中,编写一个方法,返回Inner05内部类对象
Outer05.Inner05 instance = outer05.getInstance();
}
}
class Outer05{
private int n1 = 10;
private static String name = "Tom";
static class Inner05{
public void say(){
System.out.println("name");
}
}
public void show(){
Inner05 inner05 = new Inner05();
inner05.say();
}
public Inner05 getInstance(){
return new Inner05();
}
}
枚举类(enum)
枚举是一组常量的组合
可这样理解:枚举属于一种特殊的类,里面只包含一组有限的特定的对象
枚举有两钟实现方法:
①自定义枚举
②使用enum关键字实现枚举
自定义枚举
1,不需要提供set方法,因为枚举对象通常为只读
2,对枚举对象/属性使用final+static共同修饰,实现底层优化
3,枚举对象名通常使用全部大写,常量的命名规范
4,枚举对象根据需要,也可以有多个属性
package java_fundation.enum_;
/**
* @author 子佩
* @version 1.0
*/
public class Enumeration01 {
public static void main(String[] args) {
System.out.println(Season.SPRING);
}
}
class Season{
private String name;
private String desc;
//构造器私有化
private Season(String name,String desc){
this.name=name;
this.desc=desc;
}
public static Season SPRING = new Season("春天","温暖");
public static Season WINTER = new Season("冬天","凉爽");
public static Season SUMMER = new Season("夏天","炎热");
public static Season AUTUMN = new Season("秋天","凉爽");
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
步骤
1,构造器私有化
2,本类内部创建一组对象
3,对外暴露对象(通过public final static修饰符)
4,可以提供get方法,但是不要提供set
使用enum关键字实现枚举
使用enum实现枚举,要求将定义常量对象,写在属性和构造器前面
如果有多个常量对象,用逗号间隔
package java_fundation.enum_;
/**
* @author 子佩
* @version 1.0
*/
public class Enumeration02 {
public static void main(String[] args) {
System.out.println(Season2.AUTUMN);
}
}
enum Season2{
SPRING("春天","温暖"),//自动调用构造器生成常量
WINTER("冬天","凉爽"),
SUMMER("夏天","炎热"),
AUTUMN("秋天","凉爽"),
WHAT;//自动调用无参构造器,可省略实参列表和小括号
private String name;
private String desc;
private Season2(String name, String desc){
this.name = name;
this.desc = desc;
}
private Season2(){
}
@Override
public String toString() {
return "Season2{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
使用细节
1,当我们使用enum关键字开发一个枚举类时,默认会继承Enum类
2,如果使用无参构造器创建枚举类,则实参类别和小括号可以省略
3,枚举对象必须放在枚举类的首行
常用方法
package java_fundation.enum_;
/**
* @author 子佩
* @version 1.0
*/
public class EnumMethod {
public static void main(String[] args) {
Season2 summer = Season2.SUMMER;
//name方法
System.out.println(summer.name());
//ordinal方法,输出该枚举对象的次序/编号
System.out.println(summer.ordinal());
//从反编译可以看出 values方法,返回 Season3[]
//含有定义的所有枚举对象
Season3[] values = Season3.values();
for (Season3 season : values) {
System.out.println(season);
}
System.out.println("=====valueOF=====");
//valueOf方法:将字符串转换成枚举类型,要求字符串必须是已有的常量名
Season3 summer1 = Season3.valueOf("SUMMER");//与上面的summer对象相等
System.out.println(summer1);
//compareTo方法:比较两个枚举常量,比较的就是编号
//Season3.SPRING编号-Season3.AUTUMN编号
System.out.println(Season3.SPRING.compareTo(Season3.AUTUMN));//-3
}
}
enum Season3{
SPRING("春天","温暖"),
WINTER("冬天","凉爽"),
SUMMER("夏天","炎热"),
AUTUMN("秋天","凉爽");
private String name;
private String desc;
private Season3(String name, String desc){
this.name = name;
this.desc = desc;
}
@Override
public String toString() {
return "Season2{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
/*
输出结果
SUMMER
2
Season2{name='春天', desc='温暖'}
Season2{name='冬天', desc='凉爽'}
Season2{name='夏天', desc='炎热'}
Season2{name='秋天', desc='凉爽'}
Season2{name='null', desc='null'}
=====valueOF=====
Season2{name='夏天', desc='炎热'}
-3
*/
enum实现接口
1,使用enum关键字后,就不能再继承其它类了,因为enum会隐式继承Enum类
2,枚举类和普通类一样,可以实现接口
注解(Annotation)
注解的理解
1,注解也被称为元数据,用于修饰解释器 包,类,方法,属性,构造器,局部变量等数据信息
2,和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在程序中的补充信息
3,在javaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在javaEE中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替javaEE旧版中所遗留的繁杂代码和xml配置等
三个基本的注解
1,@Override
限定某个方法,是重写父类的方法,该注解只能用于方法
2,@Deprecated
用于表示某个程序元素已过时。即不推荐使用,但是任然可以使用
可以做到新旧版本的兼容和过渡
3,@SuppressWarning
抑制编译器警告
四种元注解
元注解的介绍
JDK的元注解用于修饰其它的注解
@Retention
指定注解的作用范围,三种:SOURCE,CLASS,RUNTIME
@Target
指定注解可以在哪些地方使用
@Documented
指定该注解是否会在javadoc体现
@Inherited
子类会继承父类注解
标签:Java,name,System,OOP,println,全解,方法,public,String From: https://blog.csdn.net/zi_pei/article/details/142171268