目录
关键字
this
this可以调用类的属性、方法和构造器。
在类的方法中
可以使用"this.属性"或"this.方法"的方式,调用当前对象属性或方法。但是,通常情况下,我们都择省略"this."。特殊情况下,如果方法的形参和类的属性同名时,我们必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参。
this.name = name; //用this来区分属性和局部变量
在类的构造器中
可以使用"this.属性"或"this.方法"的方式,调用当前正在创建的对象属性或方法。但是,通常情况下,我们都择省略"this."。特殊情况下,如果构造器的形参和类的属性同名时,我们必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参。
① 在类的构造器中,可以显式的使用"this(形参列表)"方式,调用本类中指定的其他构造器。
② 构造器中不能通过"this(形参列表)"方式调用自己。
③ 如果一个类中有n个构造器,则最多有 n - 1构造器中使用了"this(形参列表)"。
④ 规定:"this(形参列表)"必须声明在当前构造器的首行。
⑤ 构造器内部,最多只能声明一个"this(形参列表)",用来调用其他的构造器。⑥ 使用 this 访问属性和方法时,如果在本类中未找到,会从父类中查找。
class Person{
String name;
int age;
int id;
// 构造器内 形参和成员变量同名,必须使用this指明该变量是成员变量
Person(String name ,int age){
this.name = name;
this.age = age;
}
// 方法内 形参与成员变量没有同名时,可使用this也可以省略
public void getInfo(){
System.out.println("姓名:" + name);
this.speak();
}
public void speak(){
System.out.println("年龄:" + this.age);
}
// 当前对象操作本方法时,用this指明
public boolean compare(Person p){
return this.name == p.name;
}
//this可以作为一个类中构造器的互相调用
public Person(){ //无参构造器
System.out.println("666");
}
public Person(String name){
this(); //调用本类中的无参构造器
this.name = name;
}
public Person(String name ,int age ,int id){
this(name, age); //调用两个参数的构造器
this.id = id;
}
}
super
super调用属性、方法:
●可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."。
●特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性。
●特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
super调用构造器:
●可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器。
●"super(形参列表)"的使用,必须声明在子类构造器的首行!
●在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二一,不能同时出现。
●在构造器的首行,没显式的声明"this(形参列表)"或"super(形参列表)",则默认调用的是父类中空参的构造器:super()
●在类的多个构造器中,至少一个类的构造器中使用了"super(形参列表)",调用父类中的构造器。
●如果子类构造器中既未显式调用父类或本类的构造器 且父类中又没有无参的构造器则编译出错。
class Person {
protected String name = "666";
protected 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, 18, null);
}
public Person(String name){
this(name, 18);
}
public String getInfo() {
return "Name: "+ name + "\nage: " + age;
}
}
class Student extends Person {
protected String name = "有点6";
private String school = "哈佛";
//super调用父类中的构造器
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;
}
public String getSchool() {
return school;
}
//调用的是父类的getInfo()
public String getInfo() {
return super.getInfo() + "\nschool: " + school;
}
}
this与super的区别
package和import
package:包
●提供包的概念是为了更好的实现项目中类的管理,将功能相近的类划分到同一个包中,包可以包含类和子包划分项目层次便于管理,解决类命名冲突的问题,控制访问权限。
●使用package声明类或接口所属的包,声明在源文件的首行。
●包属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)、“见名知意”。
●每"."一次,就代表一层文件目录。
package 顶层包名.子包名;
import:导入
●在源文件中显式的使用import结构导入指定包下的类、接口。
●声明在包的声明和类的声明之间。
●如果需要导入多个结构,则并列写出即可。
●可以使用"xxx.*"的方式,表示可以导入xxx包下的所结构。
●如果使用的类或接口是java.lang包下定义的,则可以省略import结构。
●如果使用的类或接口是本包下定义的,则可以省略import结构。
●如果在源文件中,使用了不同包下的同名的类,则必须至少一个类需要以全类名的方式显示。
●使用"xxx.*"方式表明可以调用xxx包下的所结构。但是如果使用的是xxx子包下的结构,则仍需要显式导入。
●import static:导入指定类或接口中的静态结构:属性或方法。
import 包名 .类名;
static
static 可用修饰属性 、 方法 、 代码块 、 内部类。
特点
●随着类的加载而加载。可以通过"类.静态变量"的方式进行调用。
●优先于对象存在。
●修饰的成员,被所有对象所共享。由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。
●访问权限允许时,可不创建对象,直接被类调用。
实例变量:
创建了类的多个对象时,每个对象都独立的拥一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。
静态变量(static):
创建了类的多个对象时,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。
static修饰的方法称为:静态方法或类方法
●随着类的加载而加载,可以通过"类.静态方法"的方式进行调用。
●静态方法中,只能调用静态的方法或属性。
●非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性。
●静态方法中,不能使用this和super关键字。
//静态方法中不能有this和super
class Person{
private int id;
public static int total = 0;
public static int getTotal(){
//注意静态方法内只能访问静态属性和方法,不能访问非静态的
return total;
}
public Person(){
total++;
id = total;
}
}
public class StaticTest{
public static void main(String args[]){
//不用创建对象就可以访问静态成员和静态方法
Person.total = 100;
sout(Person.getTotal());
Person p = new Person();
sout(p.total); //101
}
}
单例模式
单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。
饿汉式一:
class Bank{
//1.私有化类的构造器
private Bank(){
}
//2.内部创建类的对象,要求此对象也必须声明为静态的
private static Bank instance = new Bank();
//3.提供公共的静态的方法,返回类的对象
public static Bank getInstance(){
return instance;
}
}
饿汉式二:
class Bank{
//1.私化类的构造器
private Bank(){
}
//2.声明当前类对象,没初始化,此对象也必须声明为static的
private static Bank instance = null;
static{
instance = new Bank();
}
//3.声明public、static的返回当前类对象的方法
public static Bank getInstance(){
return instance;
}
}
懒汉式:
class Bank{
private Bank(){}
private static Bank instance = null;
public static Bank getInstance(){
/**
//方式一:效率稍差
synchronized (Bank.class) {
if(instance == null){
instance = new Bank();
}
return instance;
}
*/
//方式二:效率更高
if(instance == null){
synchronized (Bank.class) {
if(instance == null){
instance = new Bank();
}
}
}
return instance;
}
}
饿汉式:
坏处:对象加载时间过长。
好处:饿汉式是线程安全的。
懒汉式:
好处:延迟对象的创建。
单例设计模式的应用场景
●网站的计数器 ,一般也 是单例模式实现,否则难以同步。
●应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打状态,因为只能有一个实例去 操作, 否则内容不好追加。
●数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源 。
●项目中, 读取配置文件的类 ,一般也只有一个对象。没有必要每次使用配置文件数据 ,都生成一 个对象去读取 。
●Application 也是单例的典型应用。
●Windows 的 Task Manager ( 任务管理器 就是 很典型的单例模式。
●Windows 的 Recycle Bin 回收站 也 是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
final
可以用来修饰:类、方法、变量。
final 用来修饰一个类:此类不能被其他类所继承。
比如:String类、System类、StringBuffer类
final 用来修饰方法:表明此方法不可以被重写。
比如:Object类中getClass();
final 用来修饰变量:此时的"变量"就称为是一个常量。
final修饰属性:可以考虑赋值的位置:显式初始化、代码块中初始化、构造器中初始化。
final修饰局部变量:使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给 常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
static final 用来修饰属性:全局常量。
public final int ID;
public Test() {
ID = totalNumber ; // 可在构造器中给 final 修饰的“变量”赋值
}
abstract
可以用来修饰:类、方法。
abstract修饰类:抽象类
> 此类不能实例化。
> 抽象类中一定有构造器,便于子类实例化时调用。
> 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作 --->抽象的使用前提:继承性
abstract修饰方法:抽象方法
> 抽象方法只有方法的声明,没方法体。
> 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
> 若子类重写了父类中的所有的抽象方法后,此子类方可实例化。
> 若子类没重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰。
注意:
abstract不能用来修饰:属性、代码块、构造器等结构。
abstract不能用来修饰私方法、静态方法、final的方法、final的类。
模板方法设计模式
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
abstract class Template{
//计算某段代码执行所需要花费的时间
public void spendTime(){
long start = System.currentTimeMillis();
this.code(); //不确定的部分、易变的部分
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));
}
public abstract void code();
}
class SubTemplate extends Template{
@Override
public void code() {
for(int i = 2;i <= 1000;i++){
boolean isFlag = true;
for(int j = 2;j <= Math.sqrt(i);j++){
if(i % j == 0){
isFlag = false;
break;
}
}
if(isFlag){
System.out.println(i);
}
}
}
}
//抽象类的应用:模板方法的设计模式
public class TemplateMethodTest {
public static void main(String[] args) {
BankTemplateMethod btmd = new DrawMoney();
btmd.process();
BankTemplateMethod btmm = new ManageMoney();
btmm.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("我要存钱!");
}
}
模板方法设计模式是编程中经常用得到的模式 。各个框架 、类库中都有他的影子比如常见的有:
数据库访问的封装。
Junit 单元测试。
JavaWeb 的 Servlet 中关于 doGet/doPost 方法调用。
Hibernate 中模板程序。
Spring 中 JDBCTemlate 、 HibernateTemplate 等。
interface
接口 interface 是抽象方法和常量值定义的集合 。
接口的特点:
●用 interface 来定义 。接口和类是两个并列的结构
●接口中的所有成员变量都默认是由public static final 修饰的 。
●接口中的所有抽象方法都默认是由public abstract 修饰的 。
●接口中没有构造器,意味着接口不能实例化。
●接口与接口之间可以继承,而且可以多继承。
●Java类可以实现多个接口。
格式:class AA extends BB implements CC, DD, EE{ //... }
●接口的具体使用体现多态性。
●接口实际上可以看作是一种规范。
JDK7及以前:只能定义全局常量和抽象方法。
>全局常量:public static final修饰的,但是书写时,可以省略不写。
>抽象方法:public abstract修饰的,可省略不写。
JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法。
//举例一:
interface Runner{
public void start();
public void run();
}
interface Swimmer{
public double swim();
}
class Person {
public void eat(){
//...
}
}
class Man extends Person implements Runner, Swimmer{
public void start(){
//...
}
public void run(){
//...
}
public double swim(){
//...
}
}
public class Test{
public static void main(String args[]){
Test t = new Test();
Man m = new Man();
t.r(m);
t.s(m);
t.e(m);
}
public void r(Runner r){
r.start();
r.run();
}
public void s(Swimmer s){
s.swim();
}
public void e(Person p){
p.eat();
}
}
//举例二:
interface MyInterface extends Runner{
//...
}a
Java8中关于接口的新规范
1:接口中定义的静态方法,只能通过接口来调用。
2:通过实现类的对象,可以调用接口中的默认方法。如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法。
3:如果子类(或实现类)继承的父类与接口中声明了同名同参数的默认方法,那么子类在没重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则
4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没重写此方法的情况下,报错。-->接口冲突。所以这就需要我们必须在实现类中重写此方法。
5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法。
public interface A{ default void method(){...} }
public interface B{ default void method(){...} }
class SupClass { public void method(){...} }
class SubClass extends SubClass implements A, B{
public void method(){...}
public void myMethod(){
method(); //调用的是自己定义重写的方法
super.method();//调用的是父类中的
A.super.method();//A接口中的默认方法
B.super.method();//B接口中的默认方法
}
}
代理模式
代理模式是 Java 开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。
应用场景:
安全代理: 屏蔽对真实角色的直接访问 。
远程代理: 通过代理类处理远程方法调用(RMI)。
延迟加载: 先加载轻量级的代理对象 真正需要再加载真实对象。
比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时用 proxy 来进行大图片的打开 。
分类
静态代理(静态定义代理类)
动态代理(动态生成代理类)
interface Network {
public void browse();
}
//被代理类
class RealServer implements Network{
@Override
public void browse() {
System.out.println("真实服务器上网浏览信息");
}
}
//代理类
class ProxyServer implements Network {
private Network network;
public ProxyServer(Network network ){
this.network = network;
}
public void check() {
System.out.println("检查网络连接等操作");
}
public void browse() {
check();
network.browse();
}
}
public class ProxyDemo {
public static void main(String[] args ){
Network net = new ProxyServer(new RealServer());
net.browse();
}
}
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("经纪人收钱");
}
}
接口和抽象类之间的对比
补充
可变个数形参的方法
jdk 5.0新增了可变个数形参的方法。
具体使用:
●可变个数形参的格式:方法名(数据类型 ... 变量名)
●当调用可变个数形参的方法时,传入的参数个数可以是:0个,1个,2个,...
●可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载。
●可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载。换句话说,二者不能共存。
●可变个数形参在方法的形参中,最多只能声明一个可变形参,且只能声明在最后。
●可变参数方法的使用与参数部分使用数组是一致的。
public void show(int i){
System.out.println("int");
}
public void show(String s){
System.out.println("String");
}
public void show(String ... s){ //与public void show(String[] s)效果一样
//和数组一样
for(int i = 0;i < s.length;i++){
System.out.println(s[i]);
}
System.out.println("String ... s");
}
//调用
test.show(666);
test.show("老六了");
test.show("奈斯","真的六啊","老铁666");
test.show();
test.show(new String[]{"A","N","B"});
Java的值传递机制★
Java里方法的参数传递方式只有一种:值传递 。将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。
形参:
方法声明时的参数。
实参:
方法调用时实际传给形参的参数值。
形参是基本数据类型:
将实参基本数据类型变量的“数据值”传递给形参。
int a = 6;
System.out.println("修改前 a = " + a ); //6
change(a);
System.out.println("修改后 a = " + a); //6
public void change(int x){
x = 3;
}
形参是引用数据类型:
将实参引用数据类型变量的“地址值”传递给形参。
public static void main(String[] args){
Person p = new Person();
p.age = 8;
System.out.println("修改前 age = " + p.age); //8
change(p);
System.out.println("修改后 age = " + p.age); //18
}
public static void change(Person o){
o.age = 18;
}
class Person{
int age;
}
int[] a = new int[10];
System.out.println(a); //输出地址值
char[] c = new char[10];
System.out.println(c);//不是地址值,如果里面有元素将输出元素
包装类
//基本数据类型-->包装类:调用包装类的构造器
@Test
public void test1() {
int num1 = 10;
//System.out.println(num1.toString());
Integer in1 = new Integer(num1);
System.out.println(in1.toString());
Integer in2 = new Integer("123");
System.out.println(in2.toString());
}
//包装类-->基本数据类型
@Test
public void test2() {
Integer in1 = new Integer(12);
int i1 = in1.intValue();
System.out.println(i1);
Float f = new Float(122);
System.out.println(f.floatValue() + 1);
}
/*
* JDK 5.0新特性:自动装箱和拆箱
*/
@Test
public void test3() {
//自动装箱
int num = 10;
Integer n = num;
boolean b = true;
Boolean b1 = b;
//自动拆箱:包装类->基本数据类型
System.out.println(n.toString());
int numb = n;//自动拆箱
}
//基本数据类型、包装类 --> String类型:调用String重载的valueOf(Xxx xx)
@Test
public void test4() {
int num1 = 11;
//方式1:连接运算
String str1 = num1 + "";
//方式2:调用String的valueOf(Xxx xx)
float f1 = 12.23f;
String str2 = String.valueOf(f1);
Double d = new Double(12.2);
String str3 = String.valueOf(d);
System.out.println(str2);
System.out.println(str3);
}
// String类型--> 基本数据类型、包装类: 调用包装类的parseXxx()
@Test
public void test5() {
String str1 = "123";
System.out.println(Integer.parseInt(str1));
String str2 = "true";
boolean b1 = Boolean.parseBoolean(str2);
}
==和equals的区别
1、== 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型
就是比较内存地址。
2、equals 的话,它是属于 java.lang.Object 类里面的方法,如果该方法没有被重写过默认也
是==,我们可以看到 String 等类的 equals 方法是被重写过的,而且 String 类在日常开发中
用的比较多,久而久之,形成了 equals 是比较值的错误观点。
3、具体要看自定义类里有没有重写Object 的 equals 方法来判断。
4、通常情况下,重写 equals 方法,会比较类中的相应属性是否都相等。
垃圾回收机制
●垃圾回收机制只回收JVM堆内存里的对象空间。
●对其他物理连接,比如数据库连接、输入流输出流、Socket连接无能为力。
●现在的JVM有多种垃圾回收实现算法,表现各异。
●垃圾回收发生具有不可预知性,程序无法精确控制垃圾回收机制执行。
●可以将对象的引用变量设置为null,暗示垃圾回收机制可以回收该对象。
●程序员可以通过System.gc()或者Runtime.getRuntime().gc()来通知系统进行垃圾回收,会有一些效果,但是系统是否进行垃圾回收依然不确定。
●垃圾回收机制回收任何对象之前,总会先调用它的finalize方法(如果覆盖该方法,让一
个新的引用变量重新引用该对象,则会重新激活对象)。
●永远不要主动调用某个对象的finalize方法,应该交给垃圾回收机制调用。