程序员从面向过程的执行者转化成了面向对象的指挥者
面向对象分析方法分析问题的思路和步骤:
类(Class)和对象(Object)是面向对象的核心概念。
Java中用类class来描述事物也是如此。常见的类的成员有:
属性 = 成员变量 = field = 域、字段
方法 = 成员方法 = 函数 = method
创建类的对象 = 类的实例化 = 实例化类
步骤如下:
创建对象语法: 类名 对象名 = new 类名();
使用“对象名.对象成员”的方式访问对象成员(包括属性和方法)
如果创建了一个类的多个对象,对于类中定义的属性,每个对象都拥有各自的一套副本,且互不干扰
类的访问机制:
在一个类中的访问机制:类中的方法可以直接访问类中的成员变量。(例外:static方法访问非static,编译不通过)
在不同类中的访问机制:先创建要访问类的对象,再用对象访问类中定义的成员。
变量的分类:成员变量与局部变量
成员变量(属性)与局部变量的异同
相同点
不同点
成员变量(属性)和局部变量的主要区别?
成员变量 | 局部变量 | |
---|---|---|
声明的位置 | 直接声明在类中 | 方法形参或内部、代码块内、构造器内等 |
修饰符 | private、public、static、final等 | 不能用权限修饰符修饰,可以用final 修饰 |
初始化值 | 有默认初始化值 | 没有默认初始化值,必须显式赋值,方可使用 |
内存加载位置 | 堆空间或者静态域内 | 栈空间 |
(1)什么是方法(method、函数)
方法的分类:按照是否有形参及返回值
无返回值 | 有返回值 | |
---|---|---|
无形参 | void 方法名(){} | 返回值的类型 方法名(){} |
有形参 | void 方法名(形参列表){} | 返回值的类型 方法名(形参列表){} |
(2)方法的调用
方法通过方法名被调用,且只有被调用才会执行
方法调用的过程分析
特别说明
(3)方法的重载
概念
在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可
特点
与返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。
重载示例
//如下的4个方法构成了重载
public void getSum(int i,int j){
System.out.println("1");
}
public void getSum(double d1,double d2){
System.out.println("2");
}
public void getSum(String s ,int i){
System.out.println("3");
}
public void getSum(int i,String s){
System.out.println("4");
}
不构成重载的举例:
//如下的3个方法不能与上述4个方法构成重载
public int getSum(int i,int j){
return 0;
}
public void getSum(int m,int n){
}
private void getSum(int i,int j){
}
总结
“两同一不同”
如何判断是否构成方法的重载?
(4)可变个数形参的方法
JavaSE 5.0 中提供了Varargs(variable number of arguments)机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。
//JDK 5.0以前:采用数组形参来定义方法,传入多个同一类型变量
public static void test(int a ,String[] books);
//JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量
public static void test(int a ,String…books);
使用说明
方法名(参数的类型名 ...参数名)
可变个数形参示例
public void show(int i){
}
public void show(String s){
System.out.println("show(String)");
}
public void show(String ... strs){
System.out.println("show(String ... strs)");
for(int i = 0;i < strs.length;i++){
System.out.println(strs[i]);
}
}
//不能与上一个方法同时存在
// public void show(String[] strs){
//
// }
public void test(String[] msg){
System.out.println("含字符串数组参数的test方法 ");
}
public void test1(String book){
System.out.println("****与可变形参方法构成重载的test1方法****");
}
public void test1(String ... books){
System.out.println("****形参长度可变的test1方法****");
}
public static void main(String[] args){
TestOverload to = new TestOverload();
//下面两次调用将执行第二个test方法
to.test1();
to.test1("aa" , "bb");
//下面将执行第一个test方法
to.test(new String[]{"aa"});
}
(5)Java的值传递机制
(1)针对于方法内变量的赋值示例
@Test
public void test(){
System.out.println("***********基本数据类型:****************");
int m = 10;
int n = m;
System.out.println("m = " + m + ", n = " + n);
n = 20;
System.out.println("m = " + m + ", n = " + n);
System.out.println("***********引用数据类型:****************");
Order o1 = new Order();
o1.orderId = 1001;
Order o2 = o1;//赋值以后,o1和o2的地址值相同,都指向了堆空间中同一个对象实体。
System.out.println("o1.orderId = " + o1.orderId + ",o2.orderId = " +o2.orderId);
o2.orderId = 1002;
System.out.println("o1.orderId = " + o1.orderId + ",o2.orderId = " +o2.orderId);
}
规则:
(2)针对于方法的参数
(3)Java中参数传递机制:值传递
规则:
推广:
(4)示例及内存解析
(6)递归方法
定义
递归方法:一个方法体内调用它自身
如何理解递归方法
示例
// 例1:计算1-n之间所自然数的和
public int getSum(int n) {// 3
if (n == 1) {
return 1;
} else {
return n + getSum(n - 1);
}
}
// 例2:计算1-n之间所自然数的乘积:n!
public int getSum1(int n) {
if (n == 1) {
return 1;
} else {
return n * getSum1(n - 1);
}
}
//例3:已知一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),
//其中n是大于0的整数,求f(10)的值。
public int f(int n){
if(n == 0){
return 1;
}else if(n == 1){
return 4;
}else{
return 2*f(n - 1) + f(n - 2);
}
}
(1)构造器或者构造方法的作用
(2)构造器的特征
(3)构造器使用说明
权限修饰符 类名(形参列表){}
(4)示例
public class Person{
//构造器
public Person(){
System.out.println("Person().....");
}
public Person(String n){
name = n;
}
public Person(String n,int a){
name = n;
age = a;
}
}
(5)构造器重载
构造器一般用来创建对象的同时初始化对象
构造器重载使得对象的创建更加灵活,方便创建各种不同的对象,构造器重载示例如下:
public class Person{
public Person(String name, int age, Date d) {this(name,age);…}
public Person(String name, int age) {…}
public Person(String name, Date d) {…}
public Person(){…}
}
构造器重载,参数列表必须不同
(6)属性赋值过程
可以在很多位置对类的属性赋值,总结如下
(7)JavaBean的概念
(1)代码块的作用
(2)分类
一个类中代码块若有修饰符,则只能被static
修饰,称为静态代码块(static block),没有使用static
修饰的,称为非静态代码块
static代码块通常用于初始化static的属性
class Person {
public static int total;
static {
total = 100;//为total赋初值
}
…… //其它属性或方法声明
}
(3)静态代码块VS非静态代码块
(4)静态初始化块示例
class Person {
public static int total;
static {
total = 100;
System.out.println("in static block!");
}
}
public class PersonTest {
public static void main(String[] args) {
System.out.println("total = " + Person.total);
System.out.println("total = " + Person.total);
}
}
输出结果为:
in static block!
total = 100
total = 100
(5)实例化子类对象时,涉及到父类、子类中静态代码块、非静态代码块、构造器的加载顺序
加载顺序为:由父及子(先父类后子类),静态先行
class Father {
static {
System.out.println("11111111111");
}
{
System.out.println("22222222222");
}
public Father() {
System.out.println("33333333333");
}
}
public class Son extends Father {
static {
System.out.println("44444444444");
}
{
System.out.println("55555555555");
}
public Son() {
System.out.println("66666666666");
}
public static void main(String[] args) { // 由父及子 静态先行
System.out.println("77777777777");
System.out.println("************************");
new Son();
System.out.println("************************");
new Son();
System.out.println("************************");
new Father();
}
}
输出结果为:
11111111111
44444444444
77777777777
————————————————————————————————
22222222222
33333333333
55555555555
66666666666
————————————————————————————————
22222222222
33333333333
55555555555
66666666666
————————————————————————————————
22222222222
33333333333
class Root {
static {
System.out.println("Root的静态初始化块");
}
{
System.out.println("Root的普通初始化块");
}
public Root() {
System.out.println("Root的无参数的构造器");
}
}
class Mid extends Root {
static {
System.out.println("Mid的静态初始化块");
}
{
System.out.println("Mid的普通初始化块");
}
public Mid() {
System.out.println("Mid的无参数的构造器");
}
public Mid(String msg) {
//通过this调用同一类中重载的构造器
this();
System.out.println("Mid的带参数构造器,其参数值:" + msg);
}
}
class Leaf extends Mid {
static {
System.out.println("Leaf的静态初始化块");
}
{
System.out.println("Leaf的普通初始化块");
}
public Leaf() {
//通过super调用父类中有一个字符串参数的构造器
super("htzw");
System.out.println("Leaf的构造器");
}
}
public class LeafTest {
public static void main(String[] args) {
new Leaf();
System.out.println("——————————————————");
new Leaf();
}
}
输出结果为:
Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:htzw
Leaf的普通初始化块
Leaf的构造器
——————————————————
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:htzw
Leaf的普通初始化块
Leaf的构造器
(1)定义
Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称
(2)内部类的分类
成员内部类(静态、非静态 ) vs 局部内部类(方法内、代码块内、构造器内)
(3)成员内部类的理解
static
修饰final
修饰,表示此类不能被继承abstract
修饰(4)如何创建成员内部类的对象?
创建静态的Dog内部类的实例(静态的成员内部类)
Person.Dog dog = new Person.Dog();
创建费静态的Bird内部类的实例(非静态的成员内部类)
//Person.Bird bird = new Person.Bird();//错误的
Person p = new Person();
Person.Bird bird = p.new Bird();
(5)如何在成员内部类中调用外部类的结构?
class Person {
String name = "小明";
public void eat() {
}
//非静态成员内部类
class Bird {
String name = "杜鹃";
public void display(String name) {
System.out.println(name);//方法的形参
System.out.println(this.name);//内部类的属性
System.out.println(Person.this.name);//外部类的属性
//Person.this.eat();
}
}
}
(1)为什么要引入封装性?
(2)问题引入
当我们创建一个类的对象以后,我们可以通过"对象.属性"
的方式,对对象的属性进行赋值。这里,赋值操作要受到属性的数据类型和存储范围的制约。除此之外,没其他制约条件。但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件。这个条件就不能在属性声明时体现,我们只能通过方法进行限制条件的添加。(比如:setLegs()同时,我们需要避免用户再使用"对象.属性"的方式对属性进行赋值。则需要将属性声明为私有的(private)
(3)信息的封装和隐藏
Java中通过将数据声明为私有的(private),再提供公共的(public)方法:getXxx()和setXxx()实现对该属性的操作,以实现下述目的:
(4)封装性思想具体的代码体现
private double radius;
public void setRadius(double radius){
this.radius = radius;
}
public double getRadius(){
return radius;
}
(5)Java规定的四种权限修饰符
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | Y | |||
缺省 | Y | Y | ||
protected | Y | Y | Y | |
public | Y | Y | Y | Y |
(1)为什么要有类的继承性?(继承性的好处)
(2)继承性的格式
class A extends B {}
(3)子类继承父类以后有哪些不同?
(4)Java中继承性的说明
继承成员变量和继承方法的区别示例
class Base {
int count = 10;
public void display() {
System.out.println(this.count);
}
}
class Sub extends Base {
int count = 20;
public void display() {
System.out.println(this.count);
}
}
public class FieldMethodTest {
public static void main(String[] args) {
Sub s = new Sub();
System.out.println(s.count);//20
s.display();//20
Base b = s;
System.out.println(b == s);//true
System.out.println(b.count);//10
b.display();//20
}
}
说明如下:
(1)多态性的理解
可以理解为一个事物的多种形态
对象的多态性:父类的引用指向子类的对象(或者子类的对象赋给父类的引用)
可以直接应用在抽象类和接口上
Java引用变量有两个类型:编译时类型和运行时类型
对象的多态——在Java中,子类的对象可以替代父类的对象使用
子类可以看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)
(2)多态性的使用:虚拟方法调用
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的
有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法
Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法
编译时类型和运行时类型
编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类 的getInfo()方法。——动态绑定
(3)多态性的使用前提条件
①类的继承关系;②方法的重写
(4)多态性使用说明
对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
(5)总结
多态的作用
使用前提
成员方法
成员变量
面试题:多态是编译时行为还是运行时行为?如何证明
class Animal {
protected void eat() {
System.out.println("animal eat food");
}
}
class Cat extends Animal {
protected void eat() {
System.out.println("cat eat fish");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("Dog eat bone");
}
}
class Sheep extends Animal {
public void eat() {
System.out.println("Sheep eat grass");
}
}
public class InterviewTest {
public static Animal getInstance(int key) {
switch (key) {
case 0:
return new Cat();
case 1:
return new Dog();
default:
return new Sheep();
}
}
public static void main(String[] args) {
int key = new Random().nextInt(3);
System.out.println(key);
Animal animal = getInstance(key);
animal.eat();
}
}
面试题,以下程序的输出结果
public class InterviewTest {
public static void main(String[] args) {
Base base = new Sub();
base.add(1, 2, 3);
Sub s = (Sub)base;
s.add(1,2,3);
}
}
class Base {
public void add(int a, int... arr) {
System.out.println("base");
}
}
class Sub extends Base {
public void add(int a, int[] arr) {
System.out.println("sub_1");
}
public void add(int a, int b, int c) {
System.out.println("sub_2");
}
}
输出结果为:
sub_1
sub_2
Object类是所有Java类的根父类
如果在类的声明中未使用extends关键字指明其父类,则默认父类 为java.lang.Object
类
所的java类(除java.lang.Object类之外都直接或间接的继承于java.lang.Object
类
意味着,所的java类具有java.lang.Object
类声明的功能
public class Person {
...
}
等价于:
public class Person extends Object {
...
}
(1)equals()方法
是一个方法,而非运算符
只能适用于引用数据类型
Object类中equals()的定义:
public boolean equals(Object obj) {
return (this == obj);
}
说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同。即两个引用是否指向同一个对象实体
像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同
通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的==“实体内容”==是否相同。那么,我们就需要对Object类中的equals()进行重写
重写的原则:比较两个对象的实体内容是否相同
自定义类如何重写equals()
class User {
String name;
int age;
//重写其equals()方法
public boolean equals(Object obj) {
//首先判断两个对象的地址是否相同,假如相同,后续就无需在进行比较
if (obj == this) {
return true;
}
if (obj instanceof User) {
User u = (User) obj;
return this.age == u.age && this.name.equals(u.name);
}
return false;
}
}
重写equals()方法的规则
相关示例
public class Test {
public static void main(String[] args) {
int it = 65;
float fl = 65.0f;
System.out.println("65和65.0f是否相等?" + (it == fl)); //true
char ch1 = 'A';
char ch2 = 12;
System.out.println("65和'A'是否相等?" + (it == ch1));//true
System.out.println("12和ch2是否相等?" + (12 == ch2));//true
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println("str1和str2是否相等?" + (str1 == str2));//false
System.out.println("str1是否equals str2?" + (str1.equals(str2)));//true
// System.out.println("hello" == new java.util.Date()); //编译不通过
}
}
(2)==操作符
基本类型比较值:只要两个变量的值相等,即为true
引用类型比较引用(是否指向同一个对象):只有指向同一个对象时,==才返回true
用“==”进行比较时,符号两边的数据类型必须兼容(可自动转换的基本数据类型除外),否则编译出错
可以使用在基本数据类型变量和引用数据类型变量中
如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
如果比较的是引用数据类型变量:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
== 符号使用时,必须保证符号左右两边的变量类型一致
(3)toString()方法
toString()方法在Object类中定义,其返回值是String类型,返回类名和它的引用地址
在进行String与其它类型数据的连接操作时,自动调用toString()方法
可以根据需要在用户自定义类型中重写toString()方法
基本类型数据转换为String类型时,调用了对应包装类的toString()方法
当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
Object类中toString()的定义:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
像String、Date、File、包装类等都重写了Object类中的toString()方法。使得在调用对象的toString()时,返回"实体内容"信息
自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容"
以下程序输出结果
@org.junit.Test
public void test() {
char[] arr = new char[] { 'a', 'b', 'c' };
System.out.println(arr);//abc
int[] arr1 = new int[] { 1, 2, 3 };
System.out.println(arr1);//[I@5479e3f
double[] arr2 = new double[] { 1.1, 2.2, 3.3 };
System.out.println(arr2);//[D@27082746
}
面试题:==和equals()的区别是什么
java.lang.Object
类里面的方法,如果该方法没有被重写过默认也是==;我们可以看到String等类的equals方法是被重写过的,而且String类在日常开发中用的比较多,久而久之,形成了equals是比较值的错误观点(1)为什么要有包装类(或封装类)
(2)基本数据类型与对应的包装类
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
(3)基本数据类型、包装类与String之间的转换流程
(4)常用转换方法
字符串转换成基本数据类型
基本数据类型转换成字符串
(5)面试题
如下两个题目输出结果相同吗?各是什么:
@org.junit.Test
public void test() {
//此处三目运算符?后面两部分会涉及到类型统一、自动转换
Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1);//1.0
}
@org.junit.Test
public void test2(){
Object o2;
if (true)
o2 = new Integer(1);
else
o2 = new Double(2.0);
System.out.println(o2);//1
}
以下面试题的考点是:数据缓存的问题IntegerCache
Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[],保存了从-128~127范围的整数。如果使用自动装箱的方式,给Integer赋值的范围在-128~127范围内时,可以直接使用数组中的元素,不用再去new了。目的是提高效率。
@org.junit.Test
public void test() {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);//false
Integer m = 1;
Integer n = 1;
System.out.println(m == n);//true
Integer x = 128;//相当于new了一个对象
Integer y = 128;//相当于new了一个对象
System.out.println(x == y);//false
}
向上转型:多态
(1)为什么使用向下转型
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。如何才能调用子类特的属性和方法?使用向下转型
(2)如何实现向下转型
使用强制类型转换符:()
(3)使用时的注意事项
ClassCastException
的异常ClassCastException
的异常,我们在向下转型之前,先进行instanceof
的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型(4)instanceof的使用
(5)对象类型转换(Casting)
造型
(6)对象类型转换示例
public class Test {
public void method(Person e) { // 设Person类中没有getschool() 方法
// System.out.pritnln(e.getschool()); //非法,编译时错误
if (e instanceof Student) {
Student me = (Student) e; // 将e强制转换为Student类型
System.out.pritnln(me.getschool());
}
}
public static void main(String[] args) {
Test t = new Test();
Student m = new Student();
t.method(m);
}
}
(1)什么是方法的重写
(2)应用
重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法
(3)重写的规则
(4)方法重写示例
public class Person {
public String name;
public int age;
public String getInfo() {
return "Name: "+ name + "\n" +"age: "+ age;
} }
public class Student extends Person {
public String school;
public String getInfo() { //重写方法
return "Name: "+ name + "\nage: "+ age
+ "\nschool: "+ school;
}
public static void main(String args[]){
Student s1=new Student();
s1.name="Bob";
s1.age=20;
s1.school="school2";
System.out.println(s1.getInfo()); //Name:Bob age:20 school:school2
}
}
class Parent {
public void method1() {}
}
class Child extends Parent {
//非法,子类中的method1()的访问权限private比被覆盖方法的访问权限public小
private void method1() {}
}
public class UseBoth {
public static void main(String[] args) {
Parent p1 = new Parent();
Child c1 = new Child();
p1.method1();
c1.method1();
} }
(5)面试题:区分方法的重写和重载?
要点包括:①二者的概念;②重载和重写的具体规则③重载:不表现为多态性;重写:表现为多态性
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。
所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为==“早绑定”或“静态绑定”;而对于多态,只等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”==。
(1)从结果上来看:继承性
(2)从过程上来看
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,…直到调用了java.lang.Object
类中空参的构造器为止。正因为加载过所的父类的结构,所以才可以看到内存中父类中的结构,子类对象才可以考虑进行调用
(3)特殊说明
虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
(4)子类对象实例化示例
class Creature {
public Creature() {
System.out.println("Creature无参数的构造器");
}
}
class Animal extends Creature {
public Animal(String name) {
System.out.println("Animal带一个参数的构造器,该动物的name为" + name);
}
public Animal(String name, int age) {
this(name);
System.out.println("Animal带两个参数的构造器,其age为" + age);
}
}
public class Wolf extends Animal {
public Wolf() {
super("灰太狼", 3);
System.out.println("Wolf无参数的构造器");
}
public static void main(String[] args) {
new Wolf();
}
}
输出结果:
Creature无参数的构造器
Animal带一个参数的构造器,该动物的name为灰太狼
Animal带两个参数的构造器,其age为3
Wolf无参数的构造器
被Java语言赋予了特殊含义,用作专门用途的字符串(单词),这些关键字不能用于常量、变量、和任何标识符的名称。
(1)this是什么
(2)this调用属性、方法
(3)this调用构造器
(1)super是什么
(2)super调用属性、方法
(3)super调用构造器
(4)super示例
class Person {
protected String name = "张三";
protected int age;
public String getInfo() {
return "Name: " + name + "\nage: " + age;
}
}
class Student extends Person {
protected String name = "李四";
private String school = "New Oriental";
public String getSchool() {
return school;
}
public String getInfo() {
return super.getInfo() + "\nschool: " + school;
}
}
public class StudentTest {
public static void main(String[] args) {
Student st = new Student();
System.out.println(st.getInfo());
}
}
输出结果为:
Name: 张三
age: 0
school: New Oriental
public class Person {
private String name;
private int age;
private Date birthDate;
public Person(String name, int age, Date d) {
this.name = name;
this.age = age;
this.birthDate = d;
}
public Person(String name, int age) {
this(name, age, null);
}
public Person(String name, Date d) {
this(name, 30, d);
}
public Person(String name) {
this(name, 30);
}
}
public class Student extends Person {
private String school;
public Student(String name, int age, String s) {
super(name, age);
school = s;
}
public Student(String name, String s) {
super(name);
school = s;
}
// 编译出错: no super(),系统将调用父类无参数的构造器。
public Student(String s) {
school = s;
}
}
(5)this和super的区别
维度 | this | super |
---|---|---|
访问属性 | 访问本类中的属性,如果本类没有此属性则从父类中继续查找 | 直接访问父类中的属性 |
调用方法 | 访问本类中的方法,如果本类没有此方法则从父类中继续查找 | 直接访问父类中的方法 |
调用构造器 | 调用本类构造器,必须放在构造器的首行 | 调用父类构造器,必须放在子类构造器的首行 |
如果想让一个类的所有实例共享数据,就用类变量
类属性作为该类各个对象之间共享的变量。在设计类时,分析哪些属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法
如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用
(1)使用范围
(2)被修饰后的成员具备的特点
(3)static修饰属性:静态变量(或类变量)
(4)static修饰方法:静态方法、类方法
(5)如何判定属性和方法应该使用static关键字
(6)类变量VS实例变量内存解析
class Chinese {
String name;
int age;
static String nation;
@Override
public String toString() {
return "Chinese{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test{
public static void main(String[] args) {
Chinese.nation = "中国";
System.out.println(Chinese.nation);//中国
Chinese c1 = new Chinese();
System.out.println(c1);//Chinese{name='null', age=0}
c1.name = "姚明";
c1.age = 40;
System.out.println(c1);//Chinese{name='姚明', age=40}
Chinese c2 = new Chinese();
System.out.println(c2);//Chinese{name='null', age=0}
c2.name = "马龙";
c2.age = 30;
System.out.println(c2);//Chinese{name='马龙', age=30}
c1.nation = "CHN";
System.out.println(Chinese.nation);//CHN
c2.nation = "CHINA";
System.out.println(Chinese.nation);//CHINA
}
}
示例代码
class Person {
private int id;
private static int total = 0;
public static int getTotalPerson() {
//id++; //非法
return total;
}
public Person() {
total++;
id = total;
}
}
public class PersonTest {
public static void main(String[] args) {
System.out.println("Number of total is " + Person.getTotalPerson());
//没有创建对象也可以访问静态方法
Person p1 = new Person();
System.out.println("Number of total is " + Person.getTotalPerson());
}
}
输出结果为:
Number of total is 0
Number of total is 1
class Person {
private int id;
public static int total = 0;
public Person() {
total++;
id = total;
}
public static void main(String args[]) {
Person Tom = new Person();
Tom.id = 0;
total = 100; // 不用创建对象就可以访问静态成员
}
}
public class StaticDemo {
public static void main(String args[]) {
Person.total = 100; // 不用创建对象就可以访问静态成员
//访问方式:类名.类属性,类名.类方法
System.out.println(Person.total);
Person c = new Person();
System.out.println(c.total); //输出101
}
}
在Java中声明类、变量和方法时,可使用关键字final来修饰,表示==“最终的”==
final标记的类不能被继承。提高安全性,提高程序的可读性。
例如:String类、System类、StringBuffer类
final标记的方法不能被子类重写,比如:Object类中的getClass()
final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次
static final 用来修饰属性:全局常量
(1)final修饰类
final class A {
}
class B extends A{
//错误,不能被继承
}
final 用来修饰一个类:此类不能被其他类所继承。比如:String类、System类、StringBuffer类
(2)final修饰方法
class A{
public final void print(){
System.out.println("A");
}
}
class B extends A{
public final void print(){//错误,不能被重写
System.out.println("B");
}
}
final 用来修饰方法:表明此方法不可以被重写,比如:Object类中getClass()
(3)final修饰变量
class A{
private final String INFO = "htze";//声明字符常量
public void print(){
//The final field A.INFO cannot be assigned
INFO = "tj";
}
}
常量名要大写,内容不可修改
final 用来修饰变量:此时的"变量"就称为是一个常量
final修饰局部变量:
(4)final应用示例
public final class Test {
public static int totalNumber = 5;
public final int ID;
public Test() {
// 可在构造器中给final修饰的“变量”赋值
ID = ++totalNumber;
}
public static void main(String[] args) {
Test t = new Test();
System.out.println(t.ID);
final int I = 10;
System.out.println(I);
final int J;
J = 20;
J = 30; // 非法
}
}
public class Test {
public static void main(String[] args) {
Other o = new Other();
new Test().addOne(o);
}
public void addOne(final Other o) {
// o = new Other();
o.i++;
}
}
class Other {
public int i;
}
(1)抽象类与抽象方法概述
(2)abstract修饰类:抽象类
(3)abstract修饰方法:抽象方法
(4)抽象类的应用
抽象类是用来模型化那些父类无法确定全部实现,而是由其子类提供具体实现的对象的类
问题描述:
解决方案
public abstract class Vehicle {
public abstract double calcFuelEfficiency(); //计算燃料效率的抽象方法
public abstract double calcTripDistance(); //计算行驶距离的抽象方法
}
public class Truck extends Vehicle {
public double calcFuelEfficiency() {
//写出计算卡车的燃料效率的具体方法
}
public double calcTripDistance() {
//写出计算卡车行驶距离的具体方法
}
}
public class RiverBarge extends Vehicle {
public double calcFuelEfficiency() {
//写出计算驳船的燃料效率的具体方法
}
public double calcTripDistance() {
//写出计算驳船行驶距离的具体方法
}
}
(5)多态的应用:模板方法设计模式(TemplateMethod)
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式
可以解决的问题
示例
abstract class Template {
public final void getTime() {
long start = System.currentTimeMillis();
code();
long end = System.currentTimeMillis();
System.out.println("执行时间是:" + (end - start));
}
public abstract void code();
}
class SubTemplate extends Template {
public void code() {
for (int i = 0; i < 10000; i++) {
System.out.println(i);
}
}
}
//抽象类的应用:模板方法的设计模式
public class TemplateMethodTest {
public static void main(String[] args) {
BankTemplateMethod btm = new DrawMoney();
btm.process();
BankTemplateMethod btm2 = new ManageMoney();
btm2.process();
}
}
abstract class BankTemplateMethod {
// 具体方法
public void takeNumber() {
System.out.println("取号排队");
}
public abstract void transact(); // 办理具体的业务 //钩子方法
public void evaluate() {
System.out.println("反馈评分");
}
// 模板方法,把基本操作组合到一起,子类一般不能重写
public final void process() {
this.takeNumber();
this.transact();// 像个钩子,具体执行时,挂哪个子类,就执行哪个子类的实现代码
this.evaluate();
}
}
class DrawMoney extends BankTemplateMethod {
public void transact() {
System.out.println("我要取款!!!");
}
}
class ManageMoney extends BankTemplateMethod {
public void transact() {
System.out.println("我要理财!我这里有2000万美元!!");
}
}
应用场景
模板方法设计模式是编程中经常用得到的模式。各个框架、类库中都有他的影子,比如常见的有:
(1)接口的概述
(2)接口的特点
用interface来定义
接口中的所有成员变量都默认是由public static final
修饰的
接口中的所有抽象方法都默认是由public abstract
修饰的
接口中没有构造器
接口采用多继承机制
定义Java类的语法格式:先写extends,后写implements
class SubClass extends SuperClass implements InterfaceA{ }
一个类可以实现多个接口,接口也可以继承其它接口
实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类
接口的主要用途就是被实现类实现。(面向接口编程)
与继承关系类似,接口与实现类之间存在多态性
接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义==(JDK7.0及之前)==,而没有变量和方法的实现
public static final
的,但是书写时,可以省略不写public statract
接口中不能定义构造器,意味着接口不可以实例化
(3)说明
Java开发中,接口通过让类去实现(implements)的方式来使用
Java类可以实现多个接口 —>弥补了Java单继承性的局限性
格式:class AA extends BB implements CC,DD,EE
接口与接口之间可以继承,而且可以多继承
接口使用上满足多态性
接口,实际上就是定义了一种规范
(4)接口应用示例
class Computer {
public void transferData(USB usb) {//USB usb = new Flash();
usb.start();
System.out.println("具体传输数据的细节");
usb.stop();
}
}
interface USB {
//常量:定义了长、宽、最大最小的传输速度等
void start();
void stop();
}
class Flash implements USB {
@Override
public void start() {
System.out.println("U盘开启工作");
}
@Override
public void stop() {
System.out.println("U盘结束工作");
}
}
class Printer implements USB {
@Override
public void start() {
System.out.println("打印机开启工作");
}
@Override
public void stop() {
System.out.println("打印机结束工作");
}
}
(5)Java8中关于接口的改进
Java 8中,你可以为接口添加静态方法和默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。
静态方法:使用 static
关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中
找到像Collection/Collections或者Path/Paths这样成对的接口和类。
默认方法:默认方法使用 default
关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认方法。
JDK8接口新规范知识点汇总
(1)概述
(2)要解决的问题
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例
(3)单例设计模式的优点
由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
(4)单例设计模式-应用场景
(5)单例设计模式-代码实现
单例设计模式——饿汉式
class Singleton {
// 1.私有化构造器
private Singleton() {
}
// 2.内部提供一个当前类的实例
// 4.此实例也必须静态化
private static Singleton single = new Singleton();
// 3.提供公共的静态的方法,返回当前类的对象
public static Singleton getInstance() {
return single;
}
}
单例设计模式——懒汉式
class Singleton {
// 1.私有化构造器
private Singleton() {
}
// 2.内部提供一个当前类的实例
// 4.此实例也必须静态化
private static Singleton single;
// 3.提供公共的静态的方法,返回当前类的对象
public static Singleton getInstance() {
if (single == null) {
synchronized (Singleton.class){
if (single == null) {
single = new Singleton();
}
}
}
return single;
}
}
(6)单例设计模式两种创建方式对比
(1)概述
代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。
(2)代理模式示例
public class StaticProxyTest {
public static void main(String[] args) {
Star s = new Proxy(new RealStar());
s.confer();
s.signContract();
s.bookTicket();
s.sing();
s.collectMoney();
}
}
interface Star {
void confer();// 面谈
void signContract();// 签合同
void bookTicket();// 订票
void sing();// 唱歌
void collectMoney();// 收钱
}
class RealStar implements Star {
public void confer() {
}
public void signContract() {
}
public void bookTicket() {
}
public void sing() {
System.out.println("明星:歌唱~~~");
}
public void collectMoney() {
}
}
class Proxy implements Star {
private Star real;
public Proxy(Star real) {
this.real = real;
}
public void confer() {
System.out.println("经纪人面谈");
}
public void signContract() {
System.out.println("经纪人签合同");
}
public void bookTicket() {
System.out.println("经纪人订票");
}
public void sing() {
real.sing();
}
public void collectMoney() {
System.out.println("经纪人收钱");
}
}
(3)应用场景
(4)分类
(1)解决的问题
实现了创建者与调用者的分离,即将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的
(2)具体模式