一、JDK、JRE和JVM
1.JDK
Java Development Kit的简称,Java开发工具包,包含了JRE+工具包。
2.JRE
Java Runtime Environment的简称,Java运行时环境,只能运行Java程序,不能进行开发,包含JVM+类库。
3.JVM
Java Virtual Machine的简称,Java虚拟机,Java程序在执行时会先被编译为字节码文件,然后JVM会根据操作系统的不同,将字节码文件解释为该系统下可执行的文件,然后再去执行,所以JVM使得Java程序具备了跨平台的能力。
二、面向对象的三个特性
1.封装性
a.定义:
对用户隐藏对象内部的复杂性,只暴露简单的接口。
b.理解:
例如电视机、冰箱等电器,作为使用者我们不需要知道他们设计和实现的原理,只需要通过开关和一些按键使用它们提供的功能即可,这就是封装性的体现。而封装性在代码中的体现就是通过权限修饰符,将属性私有化,然后提供公共的get和set方法来让用户获取属性。
c.举例:
public class Person {
private String name;//姓名
private int age;//年龄
private int sex;//性别(0为女,1为男)
//为需要暴露的属性提供get、set方法
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 int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
}
d.作用:
- 让代码更安全,使用更方便。
e.补充说明:
- 封装性的体现需要权限修饰符。
2.继承性
a.定义:
当多个类之间具有共同的特征,那么可以把这个共同的部分抽取成一个父类,这些类就可以通过继承这个父类,而达到“代码复用”,这样就不用在多个类中重复声明相同的部分。
b.理解:
例如要定义一个Student类和一个Teacher类,而这两个类会有一些共同的属性:姓名、性别、年龄......以及方法(行为):吃饭、睡觉......为了避免在定义两个类时重复的去写这些共有的内容,所以可以抽象一个新的类Person,其中就包含有这些共有属性和方法,Student类和Teacher类只需要继承Person类即可,而针对属性和方法在每个对象中不同的体现可以通过重写实现。
c.举例:
父类Person:
public class Person {
private String name;//姓名
private int age;//年龄
private int sex;//性别(0为女,1为男)
//为需要暴露的属性提供get、set方法
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 int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
//方法
public void eat() {
System.out.println("人要吃饭~");
}
public void sleep() {
System.out.println("人要睡觉~");
}
}
(为了减少代码长度方便查看,下面的子类中不再体现封装性)
子类Student:
public class Student extends Person {//使用extends继承Person类
//添加学生特有的属性:学号
String id;
//添加学生特有的方法:学习
public void study() {
System.out.println("好好学习,天天向上!");
}
}
子类Teacher:
public class Teacher extends Person {
//添加老师特有的属性:工资
int salary;
//添加老师特有的方法:教学
public void teach() {
System.out.println("老师要认真讲课~");
}
//重写父类Person中的“eat”方法
public void eat() {
System.out.println("老师吃的可能多一点~");
}
}
测试类Test:
public class Test {
public static void main(String[] args) {
Student s = new Student();
Teacher t = new Teacher();
s.setName("Tom");
s.eat();
t.setName("John");
t.eat();
}
}
运行结果:
结果分析:
可以看到两个子类中可以直接调用父类中构建的方法,且子类Teacher中调用的eat方法是重写过后的。
d.作用:
- 实现代码复用
e.补充说明:
- 一个子类只能继承一个直接父类,但是一个父类可以被很多子类继承。
- 子类会继承父类所有的属性和方法,包括私有属性和方法,只是由于封装性的原因,无法直接使用;但是子类不会继承父类的构造器。
- Java支持多层继承,Java类的根父类为java.lang.Object。
- 子类一定会调用父类的构造器,默认调用父类的空参构造器,也可使用super手动调用;若父类中没有空参构造器,则子类中必须手动调用父类的有参构造器。
3.多态性
a.定义:
对象的多态性,父类的引用指向子类的对象。
b.理解:
还是上面的三个类:Person(父类),Student、Teacher(子类)。我们知道在构建方法设置形参时,我们需要声明形参的类型,但是如果我们只能确定需要使用到“吃饭”的方法,无法确定是Student还是Teacher,这时我们就可以将形参声明为Person类型,而在传入形参的时候新建我们需要的对象(Student或者Teacher)。
c.举例:
父类子类设计同继承性举例,这里只给出测试类Test:
public class Test {
public static void main(String[] args) {
//多态性的体现一:声明为Person类,构建子类对象
Person p1 = new Student();
Person p2 = new Teacher();
System.out.println("多态性的体现一:");
System.out.print("p1.eat():");
p1.eat();
System.out.print("p2.eat():");
p2.eat();
//多态性的体现二:声明Person类形参,传入子类对象
Test test = new Test();
test.eatTest(new Student());
}
public void eatTest(Person p) {
System.out.println("多态性的体现二:");
p.eat();
}
}
运行结果:
结果分析:
可以看到,声明的时候声明为Person类,但是执行的时候执行的是子类中重多态写过的方法,所以多态是一个运行时行为。
d.作用:
- 让代码更加灵活。
e.补充说明:
- 实现多态性的前提是必须存在类的继承关系,且存在方法的重写。
- 多态只适用于方法,不适用于属性。
- 通过多态性调用方法的时候只能调用父类和子类共有的方法,子类特有的方法无法调用,不过需要明确的是内存中是加载了这些子类特有的属性和方法的,仅仅只是无法调用。
- 可以通过向下转型来调用子类特有的属性和方法,通过代码实例来说明:
-
public class Test { public static void main(String[] args) { //多态可以看做是向上转型,将子类提升为父类,这个是自动完成的 Person p1 = new Student(); Person p2 = new Teacher(); //既然要使用子类中特有的属性和方法,那么理论上需要将父类转换为子类,这就是向下转型 //要实现向下转型需要用到强制类型转换符,如下: Student s = (Student)p1; //将Person类强制转换为Student类,便可以调用Student类特有的方法study s.study(); //但是强制转换是存在风险的,比如p1只能转换为Student而不能转换为Teacher,否则会报错 //所以在强制转换之前我们要先用关键字instanceof判断它是否属于这一类,举例: if(p2 instanceof Student) { Student s1 = (Student)p2; s1.study(); System.out.println("p2是Student类。"); } if(p2 instanceof Teacher) { Teacher t1 = (Teacher)p2; t1.teach(); System.out.println("p2是Teacher类。"); } } }
-
运行结果:
-
三、权限修饰符
作用:用于限定对象对该类成员的访问权限。
1.private
范围:被声明为private的成员只能在类的内部被调用。
2.缺省(即默认)
范围:被声明为缺省的成员在同一个类内部、同一个包中可被调用。
3.protected
范围:被声明为protected的成员在同一个类内部、同一个包中、不同包的子类中可被调用。
4.public
范围:被声明为public的成员可以在任意地方被调用,即同一个类内部,同一个包中、不同包的子类中以及同一个工程中。
注:
- 对于class的权限修饰只能用public和缺省。
四、重载与重写
1.重载(Overload)
a.定义:
在一个类中允许同时存在一个以上的同名方法,只要这些方法的参数个数或类型不同即可。
(注:形参的顺序不同也可构成重载,但是不能通过返回值类型的不同判定重载,且子类可以重载父类中的方法。)
b.要求:
- 方法名相同,但是参数的个数或类型或顺序要不同
c.举例:
public class OverLoadTest {
//先定义一个add方法
public static int add(int a,int b) {
return a + b;
}
//定义与第一个方法名相同,但是参数类型不同的方法
public static double add(double a,double b) {
return a + b;
}
//定义与第一个方法名相同,但是参数个数不同的方法
public static int add(int a,int b,int c) {
return a + b + c;
}
//接下来举例参数顺序不同的情况
//先定义一个方法
public static int add(int a,double b) {
return (int)(a + b);
}
//定义与上一个方法名相同,但是参数顺序不同
public static int add(double a,int b) {
return (int)(a + b);
}
//既然参数长度不同可以构成重载,则可以定义不定长参数
public static int add(int... a) {
int s = 0;
for(int i=0;i<a.length;i++) {//根据参数个数循环
s += a[i];//将每个参数累加
}
return s;//返回累加结果
}
}
2.重写(Override/Overwrite)
a.定义:
子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作。重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
b.要求:
- 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
- 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
>特殊情况:子类不能重写父类中声明为private权限的方法 - 返回值类型:
>父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
>父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
>父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double) - 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
c.举例:
父类Dad:
public class Dad {
public void show(){
System.out.println("我是父亲");
}
}
子类Son:
public class Son extends Dad{
public void show() {
System.out.println("我是儿子");
}
}
测试类Test:
public class Test {
public static void main(String[] args) {
Son s = new Son();
s.show();
}
}
运行结果:
3.重载与重写的异同:
重载和重写都是实现多态的方式,只是重载实现了编译时的多态,而重写实现了运行时的多态;而且重载发生在同一个类中,是让类以统一的方式处理不同类型数据的手段,而重写发生在父类与子类之间,是父类提供的方法无法满足子类的需求,子类将父类提供的方法进行修改以达到自己的需求的手段;重载要求同名的方法有不同的参数列表,且对返回值类型不做要求,而重写要求子类重写方法与父类被重写方法有相同的参数列表,且要求有兼容的返回值类型,权限修饰符不小于父类的权限修饰符,不能比父类抛出的异常更多。
五、== 和equals()方法的区别
1.==
- 可以使用在基本数据类型变量和引用数据类型变量中的运算符;
- 如果比较的是基本数据类型变量,则比较变量具体的数值是否相等,数据类型不一定要相同;
- 如果比较的是引用数据类型变量,则比较两个对象的地址值是否相等,即两个引用是否指向同一个对象实体。
- 举例:
public class Test01 { public static void main(String[] args) { //基本数据类型 int a = 10; int b = 10; double c = 10.0; char d = 10; System.out.println(a == b);//true System.out.println(a == c);//true System.out.println(a == d);//true //引用数据类型 Animal p1 = new Animal("Tom",1); Animal p2 = new Animal("Tom",1); Animal p3 = p1; System.out.println(p1 == p2);//false System.out.println(p1 == p3);//true } } class Animal{ String name; int age; public Animal(String name, int age) { this.name = name; this.age = age; } }
2.equals()方法
- 只能适用于引用数据类型
- Object类中定义的equals()方法和==的作用是相同的,比较两个对象的地址值是否相等,即两个引用是否指向同一个对象实体。
- 如String、Date、File等包装类中都对equals()方法进行了重写,重写之后比较的就是两个对象的“实体内容”是否相同。
- 举例:
public class Test01 { public static void main(String[] args) { //“==”-引用数据类型 Animal p1 = new Animal("Tom",1); Animal p2 = new Animal("Tom",1); Animal p3 = p1; System.out.println(p1 == p2);//false System.out.println(p1 == p3);//true //Object类中的equals()方法 //注:自己创建的类,因为没有显示继承其他类,所以默认继承Object类, // 所以这里调用的是Object类中的equals()方法 System.out.println(p1.equals(p2));//false System.out.println(p1.equals(p3));//true //String类中的equals()方法 String str1 = new String("菲玲爱学习"); String str2 = new String("菲玲爱学习"); System.out.println(str1.equals(str2));//true } } class Animal{ String name; int age; public Animal(String name, int age) { this.name = name; this.age = age; } }
3.总结
-
==既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址。
-
equals是属于java.lang.Object类中的方法,如果该方法没有被重写过则默认为==;而String等类中equals方法是被重写过的,是用来比较“具体值”是否相等。
-
所以具体要看是否将Object类中的equals方法给重写了,而通常情况下,重写后的equals方法,会使比较类中的相应属性是否相等。
六、Java中的常用关键字
优质资源链接:
Java常用关键字总结_lxlcnb的博客-CSDN博客_java常用关键字
七、单例设计模式
1.定义
采用一定的方法保证在整个软件系统中,某个类只存在一个实例对象,并且该实例对象只提供一个取得其对象实例的方法,我们就说这个类是单例的。
2.实现方法
- 饿汉式
a.私有化类的构造器
b.内部创建静态的类的对象
c.提供公共的静态的方法,返回类的对象
代码演示:public class SingletonTest { public static void main(String[] args) { Person person1 = Person.getFeiLing(); } } class Person{ //私有化构造器 private Person() { } //内部创建静态的类的对象 private static Person FeiLing = new Person(); //提供公共的静态的方法,返回类的对象 public static Person getFeiLing() { return FeiLing; } }
- 懒汉式
a.私有化类的构造器
b.内部声明静态的类的对象但是不初始化
c.提供公共的静态的方法,返回类的对象
代码演示:public class SingletonTest02 { public static void main(String[] args) { Teacher teacher1 = Teacher.getFeiLing() } } class Teacher{ //私有化构造器 private Teacher() { } //内部声明静态的类的对象但是不初始化 private static Teacher FeiLing = null; //提供公共的静态的方法,返回类的对象 public static Teacher getFeiLing() { FeiLing = new Teacher(); return FeiLing; } }
- 饿汉式与懒汉式实现的对比
饿汉式在一开始就会加载对象,如果此时不用就会占用内存并可能导致加载时间过程,但是饿汉式是线程安全的;懒汉式会延迟对象的创建,需要用的时候才会加载对象,缩短加载时间,但是像上面这样去实现可能会引发线程不安全的问题。