一、基础原理
1、java跨平台的原理?
java在操作系统上加入了jvm虚拟机,程序运行在jvm上,jvm会自动根据不同操作系统将相同的.class字节码解释成不同的机器码
2、JDK、JRE、JVM的区别和联系?
JDK(java development kit) >JRE(java runtime enviroment) >JVM(java virtual machine).
JDK用于开发Java程序,JRE是Java运行环境; JVM是JRE的子集,JRE是JDK的子集。
3、java程序的开发和执行过程
编写源文件->编译源文件(使用编译器javac)->字节码->执行字节码(使用解释器java)
二、java数据类型
引用数据类型的大小统一为4个字节,记录的是其引用对象的地址!
float类型赋值时必须需要添加后缀F/f。没有后缀F/f的浮点数值默认为double类型。
浮点类型float,double的数据不适合在不容许舍入误差的金融计算领域。如果需要进行不产生舍入误差的精确数字计算,需要使用BigDecimal类。
char 类型用来表示在Unicode编码表中的字符。Unicode编码被设计用来处理各种语言的文字,它占2个字节,可允许有65536个字符。
在Java中使用单引号来表示字符常量。例如’A’是一个字符,它与”A”是不同的,”A”表示含有一个字符的字符串。
boolean类型有两个常量值,true和false,在内存中占一位(不是一个字节)。
i ++:先进行赋值,再进行i+1操作; ++i:先进行i+1,再进行赋值。
逻辑与、逻辑或、逻辑非的优先级一定要熟悉!(逻辑非>逻辑与>逻辑或)。如,a||b&&c的运算结果是:a||(b&&c),而不是(a||b)&&c 。
三、控制语句
顺序、选择(if、switch)、循环
1、if → else if → else
2、switch
当布尔表达式是等值判断的情况,可以使用if → else if → else多选择结构或者switch结构;如果布尔表达式区间判断的情况,则只能使用if → else if → else多选择结构。
switch语句中case标签在JDK1.5之前必须是整数(long类型除外)或者枚举,不能是字符串,在JDK1.7之后允许使用字符串(String)。
3、while及do while
4、for循环
for(;;)与while(true)均表示无限循环;
5、break 和 continue
break 用于强行退出循环,不执行循环中剩余的语句。打破了最小封闭for或while循环。
continue 用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。
注意:
1. continue用在while,do-while中,continue 语句立刻跳到循环首部,越过了当前循环的其余部分。
2. continue用在for循环中,跳到for循环的迭代因子部分。
带标签的break 和 continue!!!
标签”是指后面跟一个冒号的标识符,例如:“label:”。对Java来说唯一用到标签的地方是在循环语句之前。而在循环之前设置标签的唯一理由是:我们希望在其中嵌套另一个循环,由于break和continue关键字通常只中断当前循环,但若随同标签使用,它们就会中断到存在标签的地方。
6、方法
方法就是一段用来完成特定功能的代码片段,类似于其它语言的函数。方法用于定义该类或该类的实例的行为特征和功能实现。 方法是类和对象行为特征的抽象。
如果方法前不加static则内存会放在堆?因此需要new一个对象才能使用,而使用static才会放在方法区,不需要new直接使用。
方法的重载是指一个类中可以定义多个方法名相同,但参数不同的方法。 调用时,会根据不同的参数自动匹配对应的方法。 重载的方法,实际是完全不同的方法,只是名称相同而已!
public class Temp {
public static void main(String[] args) {
System.out.println(add(3, 5));// 8
System.out.println(add(3, 5, 10));// 18
System.out.println(add(3.0, 5));// 8.0
System.out.println(add(3, 5.0));// 8.0
// 我们已经见过的方法的重载
System.out.println();// 0个参数
System.out.println(1);// 参数是1个int
System.out.println(3.0);// 参数是1个double
}
/** 求和的方法 */
public static int add(int n1, int n2) {
int sum = n1 + n2;
return sum;
}
// 方法名相同,参数个数不同,构成重载
public static int add(int n1, int n2, int n3) {
int sum = n1 + n2 + n3;
return sum;
}
// 方法名相同,参数类型不同,构成重载
public static double add(double n1, int n2) {
double sum = n1 + n2;
return sum;
}
// 方法名相同,参数顺序不同,构成重载
public static double add(int n1, double n2) {
double sum = n1 + n2;
return sum;
}
//编译错误:只有返回值不同,不构成方法的重载
public static double add(int n1, int n2) {
double sum = n1 + n2;
return sum;
}
//编译错误:只有参数名称不同,不构成方法的重载
public static int add(int n2, int n1) {
double sum = n1 + n2;
return sum;
}
}
7、递归
递归的基本思想就是“自己调用自己”,一个使用递归技术的方法将会直接或者间接的调用自己。
递归结构包括两个部分:
1.定义递归头。解答:什么时候不调用自身方法。如果没有头,将陷入死循环,也就是递归的结束条件。
2.递归体。解答:什么时候需要调用自身方法。
public class Temp {
public static void main(String[] args) {
long d1 = System.currentTimeMillis();
System.out.printf("%d阶乘的结果:%s%n", 10, factorial(10));
long d2 = System.currentTimeMillis();
System.out.printf("递归费时:%s%n", d2-d1); //耗时:32ms
long d3 = System.currentTimeMillis();
int a = 10;
int result = 1;
while (a > 1) {
result *= a * (a - 1);
a -= 2;
}
long d4 = System.currentTimeMillis();
System.out.println(result);
System.out.printf("普通循环费时:%s%n", d4 - d3);
}
/** 求阶乘的方法*/
static long factorial(int n){
if(n==1){//递归头
return 1;
}else{//递归体
return n*factorial(n-1);//n! = n * (n-1)!
}
}
}
递归的缺陷
简单的程序是递归的优点之一。但是递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环慢的多,所以在使用递归时要慎重。
任何能用递归解决的问题也能使用迭代解决。当递归方法可以更加自然地反映问题,并且易于理解和调试,并且不强调效率问题时,可以采用递归;在要求高性能的情况下尽量避免使用递归,递归调用既花时间又耗内存。
// 课后题目
import java.util.Scanner;
public class Temp {
public static void main(String[] args) {
//1. 从键盘输入某个十进制整数数,转换成对应的二进制整数并输出。
System.out.println("请输入一个十进制整数:");
Temp methods = new Temp();
int temp1 = methods.decimalConvertBinal(1, 0); //临时存储返回值
System.out.println("二进制结果为:" + temp1);
//2. 编程求:∑1+∑2+……+∑100。
int temp2 = methods.totalNum(); //temp2存储返回值
System.out.println("∑1+∑2+……+∑100 = " + temp2);
//3.编写递归算法程序:一列数的规则如下: 1、1、2、3、5、8、13、21、34...... 求数列的第40位数是多少。
System.out.println("第40位数字是:" + methods.sequence(40));
}
/*
* 第一题方法
* 十进制转换为二进制方法
*/
int decimalConvertBinal(int i,int changeNum) { //changeNum为二进制数,此方法返回值为二进制
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();
for(;;) {
if(num == 0) {
break;
}
else {
int remainNum = num % 2; //余数
num = num / 2; //以商作为被除数
changeNum += remainNum * i;
i = i * 10;
}
}
return changeNum;
}
/*
* 第二题方法
* 求和方法
*/
int totalNum() {
int sum = 0;
for(int j = 1;j <= 100;j++) {
for(int i = 1;i <= j;i++) {
sum += i;
}
}
return sum;
}
/*
* 第三题方法
* 数列递归方法
*/
int sequence(int num) {
if(num == 1 || num == 2) {
return 1;
}else {
return (sequence(num - 1) + sequence(num - 2));
}
}
}
四、类和对象
1、基本概念
1.对象是具体的事物;类是对对象的抽象;
2.类可以看成一类对象的模板,对象可以看成该类的一个具体实例。
3.类是用于描述同一类型的对象的一个抽象概念,类中定义了这一类对象所应具有的共同的属性、方法。
//创建一个电脑类
class Computer{
String brand;
}
public class Stu {
//fields 属性
int id;
int age;
String sname;
Computer comp;
//方法
void study() {
System.out.println("姓名:"+sname+"\n"
+"学号: "+id+"\n"
+"年龄: "+age+"\n"
+"电脑:"+comp.brand);
}
//构造方法
Stu() {
}
public static void main(String[] args) {
Stu xm = new Stu(); //创建一个学生对象
Computer dn = new Computer(); //创建一个电脑对象
dn.brand = "联想";
xm.comp = dn; //小明学生的电脑是上面创建的电脑对象
xm.id = 211;
xm.age = 18;
xm.sname = "小明";
xm.study();
}
}
2、构造方法
构造器也叫构造方法(constructor),用于对象的初始化。构造器是一个创建对象时被自动调用的特殊方法,目的是对象的初始化。
/**
1. 定义一个“点”(Point)类用来表示二维空间中的点(有两个坐标)。要求如下:
(1) 可以生成具有特定坐标的点对象。
(2) 提供可以设置坐标的方法。
(3)提供可以计算该“点”距另外一点距离的方法。
*/
class Point {
double x, y;
//构造方法,创建对象时被自动调用;用于初始化对象
public Point(double _x, double _y) {
x = _x;
y = _y;
}
//计算距离的方法
public double getDistance(Point p) {
return Math.sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
}
}
public class TestConstructor {
public static void main(String[] args) {
Point p = new Point(3.0, 4.0);//创建一个对象,初始点为3.0,4.0
Point origin = new Point(0.0, 0.0);
System.out.println(p.getDistance(origin));
}
}
3、内存分析
Java虚拟机的内存可以分为三个区域:栈stack、堆heap、方法区method area。
栈的特点如下:
1. 栈描述的是方法执行的内存模型。每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)
2. JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)
3. 栈属于线程私有,不能实现线程间的共享!
4. 栈的存储特性是“先进后出,后进先出”
5. 栈是由系统自动分配,速度快!栈是一个连续的内存空间!
堆的特点如下:
1. 堆用于存储创建好的对象和数组(数组也是对象)
2. JVM只有一个堆,被所有线程共享
3. 堆是一个不连续的内存空间,分配灵活,速度慢!
方法区(又叫静态区)特点如下:
1. JVM只有一个方法区,被所有线程共享!
2. 方法区实际也是堆,只是用于存储类、常量相关的信息!
3. 用来存放程序中永远是不变或唯一的内容。(类信息【Class对象】、静态变量、字符串常量等)。
4、this
this的本质就是“创建好的对象的地址”! 由于在构造方法调用前,对象已经创建。因此,在构造方法中也可以使用this代表“当前对象” 。
this最常的用法:
1. 在程序中产生二义性之处,应使用this来指明当前对象;普通方法中,this总是指向调用该方法的对象。构造方法中,this总是指向正要初始化的对象。
public class User {
int id; //id
String name; //账户名
String pwd; //密码
public User() {
}
public User(int id, String name) {
System.out.println("正在初始化已经创建好的对象:"+this);
this.id = id; //不写this,无法区分局部变量id和成员变量id
this.name = name;
}
public void login(){
System.out.println(this.name+",要登录!"); //不写this效果一样
}
public static void main(String[] args) {
User u3 = new User(101,"高小七");
System.out.println("打印高小七对象:"+u3);
u3.login();
}
}
2. 使用this关键字调用重载的构造方法,避免相同的初始化代码。但只能在构造方法中用,并且必须位于构造方法的第一句。
public class TestThis {
int a, b, c;
TestThis() {
System.out.println("正要初始化一个Hello对象");
}
TestThis(int a, int b) {
// TestThis(); //这样是无法调用构造方法的!
this(); // 调用无参的构造方法,并且必须位于第一行!
a = a;// 这里都是指的局部变量而不是成员变量
// 这样就区分了成员变量和局部变量. 这种情况占了this使用情况大多数!
this.a = a;
this.b = b;
}
TestThis(int a, int b, int c) {
this(a, b); // 调用带参的构造方法,并且必须位于第一行!
this.c = c;
}
void sing() {
}
void eat() {
this.sing(); // 调用本类中的sing();
System.out.println("你妈妈喊你回家吃饭!");
}
public static void main(String[] args) {
TestThis hi = new TestThis(2, 3);
hi.eat();
}
}
3. this不能用于static方法中。this指对象,不能用于类。
5、static
static修饰的成员变量和方法,从属于类。
普通变量和方法从属于对象。
在类中,用static声明的成员变量为静态成员变量,也称为类变量。 类变量的生命周期和类相同,在整个应用程序执行期间都有效。它有如下特点:
1. 为该类的公用变量,属于类,被该类的所有实例共享,在类被载入时被显式初始化。
2. 对于该类的所有对象来说,static成员变量只有一份。被该类的所有对象共享!!
3. 一般用“类名.类属性/方法”来调用。(也可以通过对象引用或类名(不需要实例化)访问静态成员。)
4. 在static方法中不可直接访问非static的成员。(在static的方法中不可调用非static的方法!)
public class User2 {
int id; // id
String name; // 账户名
String pwd; // 密码
static String company = "北京尚学堂"; // 公司名称
public User2(int id, String name) {
this.id = id;
this.name = name;
}
public void login() {
printCompany();
System.out.println(company);
System.out.println("登录:" + name);
}
public static void printCompany() {
// login();//调用非静态成员,编译就会报错
System.out.println(company);
}
public static void main(String[] args) {
User2 u = new User2(101, "高小七");
User2.printCompany();
User2.company = "北京阿里爷爷";
User2.printCompany();
u.printCompany();//静态方法一般用"类名.类属性/方法"来调用!
u.login();
}
}
6、static初始化块???
构造方法用于对象的初始化!静态初始化块,用于类的初始化操作!在静态初始化块中不能直接访问非static成员。
注意事项:
静态初始化块执行顺序(学完继承再看这里):
1. 上溯到Object类,先执行Object的静态初始化块,再向下执行子类的静态初始化块,直到我们的类的静态初始化块为止。
2. 构造方法执行顺序和上面顺序一样!!
public class User3 {
int id; //id
String name; //账户名
String pwd; //密码
static String company; //公司名称
static {
System.out.println("执行类的初始化工作");
company = "北京尚学堂";
printCompany();
}
public static void printCompany(){
System.out.println(company);
}
public static void main(String[] args) {
User3 u3 = new User3();
}
}
7、参数传值机制???
Java中,方法中所有参数都是“值传递”,也就是“传递的是值的副本”。 也就是说,我们得到的是“原参数的复印件,而不是原件”。因此,复印件改变不会影响原件。
· 基本数据类型参数的传值
传递的是值的副本。 副本改变不会影响原件。
· 引用类型参数的传值
传递的是值的副本。但是引用类型指的是“对象的地址”。因此,副本和原参数都指向了同一个“地址”,改变“副本指向地址对象的值,也意味着原参数指向对象的值也发生了改变”。
public class User4 {
int id; //id
String name; //账户名
String pwd; //密码
public User4(int id, String name) {
this.id = id;
this.name = name;
}
public void testParameterTransfer01(User4 u){
u.name="高小八";
}
public void testParameterTransfer02(User4 u){
u = new User4(200,"高三");
}
public static void main(String[] args) {
User4 u1 = new User4(100, "高小七");
u1.testParameterTransfer01(u1);
System.out.println(u1.name);
u1.testParameterTransfer02(u1);
System.out.println(u1.name);
}
}
8、包
包机制是Java中管理类的重要手段。 开发中,我们会遇到大量同名的类,通过包我们很容易对解决类重名的问题,也可以实现对类的有效管理。 包对于类,相当于文件夹对于文件的作用。
通过package实现对类的管理,package的使用有两个要点:
1. 通常是类的第一句非注释性语句。
2. 包名:域名倒着写即可,再加上模块名,便于内部管理类。
五、面向对象进阶
继承、封装、多态
1、继承
extends 扩展
package com.stu;
public class TestExtends {
public static void main(String[] args) {
Stu s1 = new Stu("小明",18,"计算机");
s1.study();
// instanceof 二元运算符 :左边是对象,右边是类;
//当对象是右面类或子类所创建对象时,返回true;否则,返回false。
System.out.println(s1 instanceof Person);
System.out.println(s1 instanceof Stu);
}
}
class Person {
int id;
String name;
int age;
public void eat() {
System.out.println(name+"吃饭!");
}
}
class Stu extends Person {
String major;
public Stu(String name, int age, String major){
this.name = name;
this.age = age;
this.major = major;
}
public void study() {
System.out.println(name+major+"学习java!");
}
}
public class Testoverride {
public static void main(String[] args) {
Vehicle v = new Vehicle();
Horse h = new Horse();
Plane p = new Plane();
v.run();
v.stop();
h.run();
h.stop();
p.run();
p.stop();
}
}
class Vehicle {
public void run() {
System.out.println("跑……");
}
public void stop() {
System.out.println("停……");
}
}
class Horse extends Vehicle {
public void run() { //重写父类方法
System.out.println("四蹄翻飞,嘚嘚嘚……");
}
}
class Plane extends Vehicle {
public void run() { //重写父类方法
System.out.println("天上飞……");
}
public void stop() { //重写父类方法
System.out.println("坠毁了……");
}
}
2、封装
需要让用户知道的才暴露出来,不需要让用户知道的全部隐藏起来,这就是封装。说的专业一点,封装就是把对象的属性和操作结合为一个独立的整体,并尽可能隐藏对象的内部实现细节。
Java是使用“访问控制符”来控制哪些细节需要封装,哪些细节需要暴露的。 Java中4种“访问控制符”分别为private、default、protected、public,它们说明了面向对象的封装性,所以我们要利用它们尽可能的让访问权限降到最低,从而提高安全性。
3、多态
父类的很多子类有同一方法,但是行为不同。比如动物类,其中狗叫是旺旺,猫叫是喵喵。
class Animal {
public void shout() {
System.out.println("叫了一声!");
}
}
class Dog extends Animal {
public void shout() {
System.out.println("旺旺旺!");
}
public void seeDoor() {
System.out.println("看门中....");
}
}
class Cat extends Animal {
public void shout() {
System.out.println("喵喵喵喵!");
}
}
public class TestPolym {
public static void main(String[] args) {
Animal a1 = new Cat(); // 向上可以自动转型
//传的具体是哪一个类就调用哪一个类的方法。大大提高了程序的可扩展性。
animalCry(a1);
Animal a2 = new Dog();
animalCry(a2);//a2为编译类型,Dog对象才是运行时类型。
//编写程序时,如果想调用运行时类型的方法,只能进行强制类型转换。
// 否则通不过编译器的检查。
Dog dog = (Dog)a2;//向下需要强制类型转换
dog.seeDoor();
}
// 有了多态,只需要让增加的这个类继承Animal类就可以了。
static void animalCry(Animal a) {
a.shout();
}
/* 如果没有多态,我们这里需要写很多重载的方法。
* 每增加一种动物,就需要重载一种动物的喊叫方法。非常麻烦。
static void animalCry(Dog d) {
d.shout();
}
static void animalCry(Cat c) {
c.shout();
}*/
}
package com.test;
public class TestMusic {
public static void main(String[] args) {
Musician m = new Musician();
Instrument i = new Instrument();
Instrument e = new Erhu();//多态
Instrument p = new Piano();
Instrument v = new Violin();
m.play(i);
m.play(e);
m.play(p);
m.play(v);
}
}
//乐手类
class Musician {
public void play(Instrument i) {// 父类引用做方法的形参,子类做实参
i.makeSound();
}
}
//乐器类
class Instrument {
public void makeSound() {
System.out.println("乐器的声音……");
}
}
//二胡,乐器的子类
class Erhu extends Instrument{
@Override //父类的方法重写,此标志可写可不写.写上有注解并自动检查的好处
//子类必须实现父类的抽象方法,否则编译错误
public void makeSound() {
System.out.println("二胡的声音……");
}
}
//钢琴,乐器的子类
class Piano extends Instrument{
@Override
public void makeSound() {
System.out.println("钢琴的声音……");
}
}
//小提琴,乐器的子类
class Violin extends Instrument{
@Override
public void makeSound() {
System.out.println("小提琴的声音……");
}
}
展示了多态最为多见的一种用法,即父类引用做方法的形参,实参可以是任意的子类对象,可以通过不同的子类对象实现不同的行为方式。
4、抽象
抽象方法:abstract修饰的方法,只有声明,没有方法体。含有抽象方法的类必须被声明为抽象类,抽象类的子类必须覆写所有的抽象方法才能被实例化,否则这个子类还是个抽象类。
子类必须要给抽象方法具体的实现。
抽象类:包含抽象方法的类就是抽象类。
//抽象类
abstract class Animal {
abstract public void shout(); //抽象方法
}
class Dog extends Animal {
//子类必须实现父类的抽象方法,否则编译错误
public void shout() {
System.out.println("汪汪汪!");
}
public void seeDoor(){
System.out.println("看门中....");
}
}
//测试抽象类
public class TestAbstractClass {
public static void main(String[] args) {
Dog a = new Dog();
a.shout();
a.seeDoor();
}
}
5、接口
接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。
public class TestInterface {
public static void main(String[] args) {
Volant v1 = new Angel();
Honest h1 = new Angel();
Honest h2 = new GoodMan();
v1.fly();
System.out.println("天使的飞行高度:"+v1.fly_hight);
h1.helpOther();
h2.helpOther();
}
}
// 飞行接口
interface Volant {
int fly_hight = 100;
void fly();
}
// 善良接口
interface Honest {
void helpOther();
}
class Angel implements Volant, Honest {
public void fly() {
System.out.println("我会飞");
}
public void helpOther() {
System.out.println("帮助别人飞");
}
}
class GoodMan implements Honest{
public void helpOther() {
System.out.println("帮助别人过马路");
}
}
接口完全支持多继承。和类的继承类似,子接口扩展某个父接口,将会获得父接口中所定义的一切。
interface A {
void testa();
}
interface B {
void testb();
}
/**接口可以多继承:接口C继承接口A和B*/
interface C extends A, B {
void testc();
}
public class Test implements C {
public void testc() {
}
public void testa() {
}
public void testb() {
}
}
面向接口编程
接口就是规范,就是项目中最稳定的东东! 面向接口编程可以让我们把握住真正核心的东西,使实现复杂多变的需求成为可能。
通过面向接口编程,而不是面向实现类编程,可以大大降低程序模块间的耦合性,提高整个系统的可扩展性和和可维护性。
6、内部类???
把一个类放在别的一个类内部定义,称为内部类。
内部类可以直接访问外部类的属性,外部类无法找到内部类声明的属性。
Java中内部类主要分为成员内部类(非静态内部类、静态内部类)、匿名内部类、局部内部类。
还有一种内部类,它是定义在方法内部的,作用域只限于本方法,称为局部内部类。
7、String类和常量池
public class TestFile1{
public static void main(String args[]) {
String s = "abc";
String ss = "abc";
String s3 = "abc" + "def";
String s4 = "abcdef";
String s5 = ss + "def";
String s6 = new String("abc");
System.out.println(s == ss); //true
System.out.println(s3 == s4);//true
System.out.println(s4 == s5);//false
System.out.println(s4.equals(s5));//true
System.out.println(s.equals(s6));//true
System.out.println(s == s6);//false
}
}