Java基本程序设计结构
java程序都是从main方法开始
.java->.class .class程序才能使用java命令来运行
int a =3 ; //用来存储整型---》计算机中使用的存储的都是二进制
编码 类似 010101张 0000000三 固定的二进制值代表着固定的编码
基本类型
int 4个字节 32位 范围正负20亿
short 2个字节 16位
long 8个字节 64位
byte 1个字节 8位 范围 -127---128
float 4个字节 32位
double 8个字节 64位
boolean
char 2个字节 ,有特定的char类型需要更多字节来表示
包装类
1.为什么要有包装类?
Java是一个面向对象的编程语言,而基本类是不是一个对象,涉及到集合的时候,基本类型用不了
2.自动拆装箱
定义:能够使基本类型与其对应的包装器类型之间自动相互转换
自动拆装箱是属于编译器行为,当编译器对 .java 源代码进行编译时,如果发现你没有进行拆箱,那么编译器来来帮你拆;如果你没有装箱,那么编译器来帮你装
public class Test { // 自动拆箱 @Test public void break() { Integer i = new Integer(100); int a = i; } // 自动装箱 @Test public void install() { Integer i = 100; } }
注:
装箱
Integer x =Integer.valueOf(9);
拆箱
Integer A=new Integer(8); int a=A.intValue();
3.128陷阱问题
128陷阱出现的原因就是Integer的valueOf方法中有一个存储[-128-127]的catch数组,这个数组就相当于一个缓存,当我们定义的数在-128-127之间时,就直接返回该内存中的地址
Integer中的valueOf方法
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
所以当我们用==比较两个用Integer定义值为128的数时,会返回false,此时a和b是封装在两个对象中,比较应用.equals()方法
几个测试样例
public class test { public static void main(String[] args) { Integer a = 1; Integer b = 1; System.out.println(a==b); } }
public class test { public static void main(String[] args) { Integer a = 1; Integer b = 2; System.out.println(a==b); } }
public class test { public static void main(String[] args) { Integer a = 128; Integer b = 128; System.out.println(a==b); System.out.println(a.equals(b)); } }
类型转换
1.自动类型转换
示例代码
int i = 128; double b = i;//128.0
2.强制类型转换
强制转换格式:(类型)变量名
注意点:
父类子类对象的强制类型转换的示例
必须是父类对象指向子类引用,强制转换成子类之后之后才能使用子类中的方法
People类
public class People { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
Student类
String
1.为什么字符串不可变?
- final修饰类
- final修饰的char数组
- 整个类内不存在任何一个对数组内容做修改的方法,即使是String的某些截取字符串的api,也是生成新的字符串对象
Java设计者认为共享带来的高效率远远胜过于提取,拼接字串带来的效率
2.Stringbuilder和StringBuffer
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
使用 StringBuffer 类时,每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象
StringBuilder-线程不安全 StringBuffer-线程安全
StringBuilder 相较于 StringBuffer 有速度优势
在多线程的情况下使用StringBuffer
final关键字
修饰类:不可以再被继承了
修饰方法:不可以被重写
修饰变量:不可以对其尽享复制,但可以通过某些骚操作对其内部的值进行更改
并发:防止指令重排序
下面是final修饰变量时,修改内部值的骚操作的例子
public class test { public static void main(String[] args) { final int [] arr = new int[]{1}; //不能这样对final修饰的变量进行修改 arr = new int[]{123}; //可以通过这样对final修饰的变量内部进行修改(骚操作) arr[0]=2; } }
static final 类常量
归类所有
枚举类型
变量的取值只在一个有限的集合内
针对这种情况,可以自定义枚举类型。枚举类型包括有限个命名的值。例如:Size s = Size.MEDIUM;
Size 类型的变量只能存储这个类型声明中给定的某个枚举值 , 或者 null 值 , null 表示这个变量没有设置任何值 。
面向对象编程
什么是面向对象编程?和面向过程有什么区别,优缺点是什么?
优点:以人类解决问题的方法,思路,习惯和步骤来设计相应的应用程序
把构成问题的各个事物分解成各个对象,提高程序的重用性,灵活性和可拓展性
三大特征
- 封装:将数据和方法结合在一起,对数据的访问者隐藏了数据的实现方式
- 继承:复用已经存在的方法和域,子类继承父类的属性和方法
- 多态:一个行为具有多个不同表现形式或形态的能力
多态实现的必要条件
1. 子类必须继承父类
2. 必须有重写
3. 父类引用指向子类对象
一个实例:
父类
public class Animal { public void eat() { System.out.println("所有的动物都很能吃。。。。。"); } public void run() { System.out.println("所有的动物都很能吃。。。。。"); } }
子类
public class Cat extends Animal{ public void eat() { System.out.println("所有的猫都能吃。。。。。"); } public void jump() { // TODO Auto-generated method stub System.out.println("所有的猫都能跳。。。。"); } }
测试类
public class Test { public static void main(String[] args) { Animal animal = new Cat() ; animal.eat(); } }
运行结果:
题外话
强制类型转换后:
public class Test { public static void main(String[] args) { Animal animal = new Cat() ; animal.eat(); //但是如果调用Cat类里有但Animal类中没有的方法就会报错 //animal.jump(); //需要强制转换成Cat类 Cat cat = (Cat) animal; cat.jump(); } }
构造函数
特征
- 要和类名一样
- 可以重载
- 如果没有定义构造函数的话,会自动生成一个无参的构造函数
- 没有返回值,连void都没有
- 单例模式下,构造函数要私有化
访问修饰符
同一个类 同一个包 不同包中的子类 不同包中的非子类 private √ default √ √ protect √ √ √ public √ √ √ √
类加载的执行顺序
从main方法所在类加载
从上往下执行静态初始化语句初始化块,每涉及到加载类的时候,最后执行构造函数
静态方法,每次调用才会执行
看一个案例:
public class InitializeDemo { private static int k = 1; private static InitializeDemo t1 = new InitializeDemo("t1"); private static InitializeDemo t2 = new InitializeDemo("t2"); private static int i = print("i"); private static int n = 99; static { print("静态块"); } private int j = print("j"); { print("构造块"); } public InitializeDemo(String str) { System.out.println((k++) + ":" + str + " i=" + i + " n=" + n); ++i; ++n; } public static int print(String str) { System.out.println((k++) + ":" + str + " i=" + i + " n=" + n); ++n; return ++i; } public static void main(String args[]) { new InitializeDemo("init"); } }
- 首先初始化静态变量,k、i、n全部为0,然后顺序执行语句 k=1,执行了
- 然后初始化t1,此时n、i均为0,所以初始化t1的成员变量j;输出 1:j i=0 n=0 2:构造块 i=1 n=1
- 再初始化t1的构造方法:3:t1 i=2 n=2
- 然后初始化t2
- 先初始化t2的成员j变量输出 4:j i=2 n=2 5:构造块 i=3 n=3
- 再初始化t2的构造方法:6:i i=5 n=5
- 执行i=print(i);输出:7:i i=6 n=6
- 再执行n=99
- 执行静态块:输出 8:静态块 i=7 n=99
- 最后初始化init的成员变量:9:j i =8 n=100 10:构造块 i=9 n=101
- 初始化init的构造方法:11:init i=10 n=102
总结
- 静态只在类加载的时候执行,且执行一次。
- 非静态只在实例化的时候执行,且每次实例化都执行。
- 静态在非静态之前执行。
- 静态属性和静态块的执行顺序取决于编码顺序,对它们一视同仁。
- 非静态属性和构造块的执行顺取决于编码顺序,对它们也一视同仁。
- 最后执行构造方法。
(静态属性=静态代码块)> (非静态属性 = 构造块)> 构造方法