JAVA 面向对象·基础语法3
学过c++的同学肯定都知道PC寄存器,它其实就是指令地址,和汇编语言中的IP寄存器差不多。
PC寄存器:存储JAVA虚拟机正在执行的字节码指令(.class文件)的地址。
JAVA虚拟机栈:存储栈帧
堆:存储GC(垃圾回收器)所管理的各种对象(一个程序中new出来的所有对象)。
方法区:存储每一个类的结构信息(比如构造方法和普通方法)。
this:是一个指向当前对象的引用。
*********this的本质是一个隐藏的 位置最靠前的方法参数。(隐藏参数)
*********只能在构造方法中使用this去引用其他构造方法。
包的本质其实就是文件夹。
在JAVA中,任何新建的类都会默认为继承JAVA的基类—Object(它的包名为 Java.lang.object)。
子类的构造方法必须先调用父类的构造方法,在执行后面的代码。
方法签名=方法名+参数类型
重写与重载——Super
在JAVA 中,重写需要注意以下几个方面:
1.子类的返回类型一定要小于或等于父类的的返回类型
Super:访问父类中定义的成员变量,可以调用父类中定义的方法(包括构造方法)。
******************JAVA 的硬性规定:子类的构造方法必须先调用父类的构造方法,再执行后面的代码。
Java中的覆盖指的是实例方法。
封装
1.成员变量private化,提供public的setter,getter
Static类
1.被static修饰:成员变量,类变量,静态变量,静态字段
(在程序运行的过程中,只占用一份固定的内存,一般存储在方法区)
2.没有被static修饰:实例变量
(每个实例内部都有一份内存,存储在堆空间)
3.被static修饰的方法:类方法,静态方法
—方法内部是不可以使用this关键字的
—可以直接访问类变量,类方法
—不可以直接使用实例变量,实例方法
—可以通过实例,类访问
4.没有被static修饰的方法:实例方法(只能通过实例调用,不能通过类名调用)
*************(实例内部必有this)
初始化块·静态初始化块
每创建一次实例时,初始化块就会去执行一次,而静态初始化块与实例是不挂钩的
*******************手动为实例变量赋上初始值:
1.在声明中
2.在构造方法中
3.在初始化块中
public class Person{
public int age;
//初始化块
{
age = 10;
}
}
//编译器会将初始化块复制到每个构造方法的头部(每创建一个实例对象,就会执行一次代码块)
*******************手动为类变量赋上初始值:
1.在声名中
2.在静态初始化块中
public class Person{
public static int count;
//静态初始化块
static{
count = 10;
}
}
//当一个类被第一次主动使用时,JVM会自动对类进行初始化
//当一个类被进行初始化时会执行静态初始代码块
以上代码运行结果如图
一段代码中可以有多个初始化块和静态初始化块,按照在源码中出现的顺序被执行
单例模式
构造一个单例模式(饿汉式单例模式):
public class Rocket{
//私有的静态的实例变量
private static Rocket instance = new Rocket();
//首先不能让外界访问你的构造方法,即构造方法私有化(就不能随便去创建对象)
private Rocket() {
//其次 提供一个公共的静态的方法,返回唯一的那个实例
public static Rocket getInstance(){
return instance;
}
}
}
构造一个单例模式(懒汉式单例模式):
public class Rocket{
private static Rocket instance = null;
private Rocket() {
if(instance==null){
instance = new Rocket();
}
return instance;
}
}
//有线程安全问题
Final类
子类对象的内部不仅有自己的成员变量,还有父类的所有成员变量。
被final修饰的类:不能被继承。
被final修饰的方法:不能被重写。
被final修饰的变量:只能进行1次赋值。
凡是static final修饰的变量都采用大写字母,若有多个单词中间需用下划线连接。
被static final修饰的可以看作一个常量。//也叫做编译时常量(compile—time constant)。
//宏替换:
宏
1.预处理:主要任务包括删除注释、插入被#include进来的文件内容、定义和替换由#define 定义的符号以及确定代码部分内容是否根据条件编译(#if )来进行编译。
2.宏定义:⑴宏常量:用#define来定义一个符号常量
⑵宏语句:定义一条或多条语句
⑶宏函数:用宏来定义函数,因为宏定义也可以带参数
⑷其他:#undef 是用来撤销宏定义的
静态导入
用一段代码实现静态导入:
import static com.mj.other.Test.*;
经典使用场景:圆周率PI的使用。
import static java.lang.Math.PI;
//import static java.lang.Math.*;
public class Main{
public static void main(String args[]){
//max(a,b);
System.out.println(" 2 * PI * 10");
}
}
但是过度使用静态导入,会产生歧义,让读者不明白这些到底是在哪个类中定义的。
嵌套类 Nested Class
嵌套类:定义在另一个类中的类,分为静态嵌套类(被static修饰)和非静态嵌套类(没有static修饰)。//***非静态嵌套类也叫内部类
外部类:在嵌套类外层的类。
顶级类:最外层的外部类。
内部类:跟实例变量,实例方法一样,内部类与外部类的实例相关联。
必须先创建外部类实例,在调用外部类实例去创建内部类实例。(记得导包)
如下图 内部类不可以定义任何static成员。(除非是编译时常量——即被static final修饰)
内部类可以直接访问外部类中的所有成员,即使该成员被声名为private。
外部类可以直接访问内部类的任何成员变量和方法,即使该成员被声名为private。
package www;
//内部类举例(公司名字,公司解雇,员工名字,序号,显示员工信息)
public class Company {
private String name;
public Company(String name) {
this.name =name;
}
public void Fire(Employee e) {
System.out.println(name+"fire"+e.number);
}
public class Employee{
private int number;
public Employee(int number) {
this.number = number;
}
public void show() {
System.out.println(name+":"+number);
}
}
}
内部类细节:当外部类和内部类中都出现了相同的变量名,若想在内部类中调用外部类的那个变量,应表示为:
public class OuterClass{
private int x = 1;
class InnerClass{
private int x = 2;
System.out.println(OterClass.this.x);
}
}
静态嵌套类 Static Nested Class
静态嵌套类:在行为上相当于顶级类,只是定义的代码写到了另一个类中。(可以理解为:借另一个类的的空间去放一下代码)
(与一般顶级类相比)静态嵌套类的特殊权限:可以直接访问外部类里除了实例变量和实例方法的其他成员,即使该成员被声名为private。
*************若静态嵌套类想要访问外部类中的实例变量和实例方法,则必须先要new一个对象,通过对象调用实例变量和方法。
什么情况下使用嵌套类?
1.如果类A只用在类C内部,可以考虑将类A嵌套在类C内部。
2.封装性更好,程序包更加简化
3.增强可读性,维护性
4.如果类A经常访问类C中的非公共成员,可以考虑将类A嵌套在类C内部。
5.也可以根据需要将类A隐藏起来,不对外暴露
6.如果类A要经常访问类C中的非公共的实例成员,则设计成内部嵌套类,否则设计为静态嵌套类。
7.如果只有实例A才能创建实例C,那么可以把C作为A的一个内部类来使用
局部类 Local Class
局部类:定义在代码块中的类(可定义在方法中,for循环中,if语句中)。
1.局部类不能定义除了编译时常量以外的任何static成员
2.局部类只能访问 final类或者 有效final类 (只进行一次赋值)的局部变量
3.从Java 8 开始,凡是没有进行第二次赋值的局部变量就被称为 有效final 。
4.局部类可以直接访问外部类中的所有成员,即使该成员被声名为private。
5.局部类只有定义在实例相关的代码块中,才能直接访问外部类中的实例成员(实例变量,方法)
抽象类
1.抽象方法
抽象方法:被abstract修饰的方法。
注意事项:1.只有方法声明,没有方法实现(参数列表后没有大括号,而是分号)
2.不能是private权限(因为定义抽象方法的目的是让子类去实现)
3.*************只能是实例方法,不能是类方法
4.只能定义在抽象类和接口中
抽象类:
1.是为了给别人继承的,所以不能使用final 修饰,也不能实例化,子类必须实现抽象父类中的所有的构造方法
**********************抽象类不能创建实例对象
2.抽象类其实可以理解为在原来的普通类基础上增加了一个新的功能——构造抽象方法
接口 Interface
API:应用编程接口,提供给开发者一组调用的功能。
而Java中的接口,是一系列方法声明的集合。(抽象方法)
implements 是类使用接口时的关键字。
接口可以定义抽象方法,常量,嵌套类型,默认方法,静态方法
上述可以定义的内容都含有隐式public,所以在写此段代码时可以省略不写
接口中的常量可以省去static final ,接口中是不能出现成员变量的。
接口中不能自定义构造方法,不能实例化,不能定义(静态)代码块。
接口名称可以在任何使用类型的地方使用,可以理解为接口也是一种类型。如果一个类实现的多个接口中有相同的抽象方法,那么只需要实现此方法一次。
接口一般是放一些行为,能力的代码块,而继承是代表你属于哪一类
在接口里写的方法都是抽象方法.
抽象类与接口的对比
抽象类
1.继承:
class A extendS D{}//A是D
2.何时选择抽象类?
⑴在紧密相关的类之间共享代码
⑵除public以外的访问权限
⑶需要创建实例变量和非final类的静态变量
接口:
1.实现:
A implements D{ //A会D中的所有行为
}
2.何时选择接口?
⑴不相关的类实现相同的方法
⑵只是定义行为,不关心是谁具体实现了这个行为
⑶想实现类型的多重继承
接口的升级问题
默认方法
1.用default修饰默认方法,并且此方法能够具体实现
2.默认方法只能是实例方法
3.重新声明默认方法,将默认方法声明为抽象方法(此类必须是抽象类)
4.如果父类的非抽象方法与接口的默认方法相同时,最终调用父类的方法
5.可以通过super关键字来调用接口的默认方法(……忘了……{{{(>_<)}}})
静态方法
1.接口中的定义的静态方法只能通过接口名调用,不能被继承
使用接口的好处
客户端→服务器→业务解析→业务层→DAO层
1.业务层调用DAO层 最好通过接口去调用。
2.涉及到了一些架构相关的问题,具体内容会在第二、三部分进行讲解。
多态
多态:具有多种形态。(开发中经常会用到接口,也可以叫做 面向接口编程)
体现:
1.父类(接口)类型指向子类对象
2.调用子类重写的方法
虚方法调用:JVM会根据引用变量指向的具体对象来调用相应的方法。(相当于c++虚函数调用)
Instanceof : 可以通过instanceof来判断某个类型是否属于某种类型。(非常常用)
父类类型指向子类对象,这个顺序是绝对不能颠倒的。
((Dog)Animal).wang();//强制转换,将animal类转化为Dog类
Dog dog1 = new Dog();
dog1.run(); //Dog-run
Animal dog2 = new Dog();
dog2.run(); //Animal-run
以上属于类方法(静态方法)调用,类方法是不看具体的实例对象的,只看对象属于哪一类。
成员变量的访问细节:
和类方法调用相似,在访问成员变量之前,也只会看对象属于所在的类,根据就近原则来进行下一步操作。
所以这样会为程序带来歧义,最好是将类中的public权限变成private。
匿名类(常用)
当接口,抽象类的实现类,只在项目中出现过一次,可以考虑使用匿名类.
public class Main{
public static void main(String args[]){
Runnable person = new Runnable() { //匿名类
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("person-run");
}
};
person.run();
}
}
匿名类不能定义除编译时常量以外的任何static成员.
匿名类不可以定义构造方法。
匿名类其实和局部类十分相似,只能访问final或者 有效final的局部变量.
举个例子(如下图)
public interface Eatable {
String name() ; //相当于抽象方法,所以格式不能出错
int enery() ;
}
public class Person {
public void eat(Eatable e) {
System.out.println("person-"+e.name()+"-"+e.enery());
}
}
public static void main(String args[]){
Person person = new Person();
person.eat(new Eatable() {
@Override
public String name() {
return "apple";
}
@Override
public int enery() {
return 50;
}
});
}
} //person-apple-50
或
public static void main(String args[]){
Person person = new Person();
Eatable beef = new Eatable() {
@Override
public String name() {
// TODO Auto-generated method stub
return "beef";
}
@Override
public int enery() {
// TODO Auto-generated method stub
return 500;
}
};
person.eat(beef);
}
} //person-beef-500
匿名类的常见用途
1.代码传递:可以节省很多空间,不需要再去创建一个新的类。
举个例子,测试一段代码所用的时间
2.过滤器
3.回调:callback,其实与代码传递相类似。
匿名类的排序
1.java的自动排序(升序)
Arrays.sort(a);
2.Java的倒序排序
Arrays.sort(a,new comparator<Integer>()){
@Override
public int compare(Integer o1, Integer o2){ //这里不能直接写int,不然就会报错
return o2-o1;
}
});
原理如下:
3.打印数组
System.out.println(Arrays.toString(a));//括号中填数组名
Lambda Expression
1.函数式接口:只包含一个抽象方法的接口。
可以在上面加上一个注解,表明它是一个函数式接口。
@FunctionalInterface
public interface Testable{
void test(int b);
}
当匿名类实现的是函数式接口时,可以使用Lambda进行简化。(相当于是用Lambda替换掉匿名类)
Lambda 的使用格式:
(参数列表) -> {
return XXXX;
}
Lambda的使用注意:
1.Lambda只能访问final或者有效final的局部变量。
2.Lambda没有引入新的作用域
匿名类与Lambda的对比
1.在作用域方面是有所区别的
方法引用
1.引用类方法
类名::方法名
2.引用特定对象的实例方法
system.out.println 的本质就是引用特定对象的实例方法。
也可以简化为 具体的对象::调用的方法名
3.引用特定类型的任意对象的实例方法
本来字符串底层就有compare方法,调用它可以比较大小
忽略大小写进行比较
也可以将上述代码简化成下面这种形式
所以引用特定类型的任意对象的实例方法是 类名::方法名
4.引用构造方法
所以引用构造方法的格式为 类名::new;
5.引用数组的构造方法
格式为 int[ ] :: new ;
6.引用当前类中定义的实例方法
格式为 this::方法名 ;
7.引用父类中定义的实例方法
格式为 super::方法名 ;
方法引用的最后总结:

浙公网安备 33010602011771号