Java工程师的工资待遇怎么样?
Java工程师的工资待遇怎么样?
Java软件工程师一般月薪范围在4000-10000元,远远超过了应届毕业生月薪 2500元的平均水平。通常来说,有一年工作经验的Java高级软件工程师的薪酬大致在年薪10—13万左右。
从Java的应用领域来分,Java语言的应用方向主要表现在以下三个方面:首 先是大中型的商业应用;其次是桌面应用,就是常说的C/S应用;再次是移动领域应用。
综上而言JAVA就业方向为:可以从事JSP网站开发、Java编程、Java游戏开 发、Java桌面程序设计,以及其他与Java语言编程相关的工作。可进入电信、银行、保险专业软件开发公司等从事软件设计和开发工作。
JavaWeb开发
JavaWeb开发
-
01HTML5与CSS3
-
1.B/S架构
-
2.HTML基本使用
-
3.HTML DOM
-
4.CSS选择器
-
5.常用样式
-
6.盒子模型与布局
-
7.HTML5新特性
-
8.CSS3新特性
-
02JavaScript
-
1.JavaScript基本语法
-
2.JavaScript流程控制
-
3.数组、函数、对象的使用
-
4.JavaScript事件绑定/触发
-
5.JavaScript事件冒泡
-
6.JavaScript嵌入方式
-
7.JavaScript DOM操作
-
8.DOM API
-
03jQuery
-
1.jQuery快速入门
-
2.jQuery语法详解
-
3.jQuery核心函数
-
4.jQuery对象/JavaScript对象
-
5.jQuery选择器
-
6.jQuery 文档处理
-
7.jQuery事件
-
8.jQuery动画效果
-
04AJAX&JSON
-
1.AJAX技术衍生
-
2.XMLHttpRequest使用
-
3.同步请求&异步请求
-
4.JSON语法
-
5.Java JSON转换
-
6.JavaScript JSON转换
-
7.jQuery 基本AJAX方法
-
8.底层$.ajax使用
-
05XML
-
1.XML用途
-
2.XML文档结构
-
3.XML基本语法
-
4.DOM&SAX解析体系
-
5.DOM4j节点查询
-
6.DOM4j文档操作
-
7.xPath语法
-
8.xPath快速查询
-
06bootstrap
-
1.bootstrap快速使用
-
2.栅格系统
-
3.表单、表格、按钮、图片
-
4.下拉菜单
-
5.按钮组使用
-
6.导航条
-
7.分页、进度条
-
07Web服务器基础
-
1.HTTP协议
-
2.HttpWatch
-
3.Tomcat服务器搭建
-
4.Tomcat目录结构解析
-
5.Tomcat端口配置
-
6.Tomcat启动&停止
-
7.Tomcat&Eclipse整合
-
8.Eclipse配置优化
-
08Servlet
-
1.Servlet体系
-
2.Servlet生命周期
-
3.ServletConfig&ServletContext
-
4.请求&响应
-
5.重定向&转发
-
6.中文乱码解决方案
-
7.项目路径问题
-
09JSP
-
1.JSP语法
-
2.JSP原理
-
3.JSP脚本片段&表达式
-
4.JSP声明&指令
-
5.JSP九大隐含对象
-
6.域对象使用
-
10JSTL
-
1.JSTL简介
-
2.JSTL-核心标签库
-
3.JSTL-函数标签库
-
4.JSTL-fmt标签库
-
5.自定义标签库使用
-
6.自定义标签库原理
-
11EL
-
1.EL表达式简介
-
2.EL使用
-
3.EL取值原理
-
4.EL的11大隐含对象
-
5.EL2.2与3.0规范
-
6.EL逻辑运算
-
7.函数库深入
-
12Cookie&Session
-
1.Cookie机制
-
2.Cookie创建&使用
-
3.Session原理
-
4.Session失效
-
5.Url重写
-
6.Session活化&钝化
-
7.Token令牌应用
-
13Filter&Listener
-
1.Filter原理
-
2.Filter声明周期
-
3.Filter链
-
4.Filter登录验证
-
5.Filter事务控制
-
6.Listener原理
-
7.八大监听器使用
-
8.Listener监听在线用户
-
14国际化
-
1.国际化原理
-
2.ResourceBundle&Locale
-
3.国际化资源文件
-
4.日期/数字/货币国际化
-
5.页面动态中英文切换
-
6.页面点击链接中英文切换
-
7.fmt标签库的使用
-
15文件上传
-
1.文件上传原理
-
2.commons-io&commons-fileupload
-
3.文件上传参数控制
-
4.文件上传路径浏览器兼容性解决
-
5.文件**原理
-
6.文件**响应头
-
7.文件**中文乱码&浏览器兼容性
创建者模式之-单例模式
>
应用场景
由于单例模式只生成一个实例, 减少了系统性能开销(如: 当一个对象的产生需要比较多的资源时, 如读取配置, 产生其他依赖对象, 则可以**在应用启动时直接产生一个单例对象, 然后永久驻留内存的方式来解决)
Windows中的任务管理器; 文件系统, 一个操作系统只能有一个文件系统; 数据库连接池的设计与实现; SPRing中, 一个Component就只有一个实例java-Web中, 一个Servlet类只有一个实例;实现要点
声明为private来隐藏构造器 private static Singleton实例 声明为public来暴露实例获取方法单例模式主要追求三个方面性能
线程安全 调用效率高 延迟加载实现方式
主要有五种实现方式,懒汉式(延迟加载,使用时初始化),饿汉式(JVM加载资源时初始化),双重检查,静态内部类,枚举。
懒汉式,线程不安全的实现由于没有同步,多个线程可能同时检测到实例没有初始化而分别初始化,从而破坏单例约束。
缺点:线程不安全,产生多个实例
public class Singleton { private static Singleton instance; private Singleton() { }; public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } 懒汉式,线程安全但效率低下的实现 优点:线程安全,延迟加载 缺点:低效, public class Singleton { private static Singleton instance; private Singleton() { }; public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }由于对象只需要在初次初始化时需要同步,多数情况下不需要互斥的获得对象,加锁会造成巨大无意义的资源消耗
双重检查 优点:线程安全,延迟加载,高效;
这种方法对比于上面的方法确保了只有在初始化的时候需要同步,当初始化完成后,再次调用getInstance不会再进入synchronized块。
public class Singleton { private static volatile Singleton instance; private Singleton() { }; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }说明: 内部检查是必要的
由于在同步块外的if语句中可能有多个线程同时检测到instance为null同时进入同步块区外部(synchronized (Singleton.class)之外),所以在进入同步块后还需要再判断是否为null,避免后续获得锁的线程再次对instance进行初始化。
instance声明为volatile类型是必要的。指示JVM不用优化。
指令重排
由于初始化操作 instance=new Singleton()是非原子操作的,主要包含三个过程
1、给instance分配内存 2、调用构造函数初始化instance 3、将instance指向分配的空间(instance指向分配空间后,instance就不为空了)
虽然synchronized块保证了只有一个线程进入同步块,但是在同步块内部JVM出于优化需要可能进行指令重排,例如(1->3->2),instance还没有初始化之前其他线程就会在外部检查到instance不为null,而返回还没有初始化的instance,从而造成逻辑错误。
volatile保证变量的可见性
volatile类型变量可以保证写入对于读取的可见性,JVM不会将volatile变量上的操作与其他内存操作一起重新排序,volatile变量不会被缓存在寄存器,因此保证了检测instance状态时总是检测到instance的**新状态。
注意:volatile并不保证操作的原子性,例如即使count声明为volatile类型,count 操作被分解为读取->写入两个操作,虽然读取到的是count的**新值,但并不能保证读取与写入之间不会有其他线程再次写入,从而造成逻辑错误
饿汉式 优点:线程安全 缺点:没有延迟加载 这种方式基于单ClassLoder机制,instance在类加载时进行初始化,避免了同步问题。饿汉式的优势在于实现简单,劣势在于不是延迟加载模式(lazy initialization)
在需要实例之前就完成了初始化,在单例较多的情况下,会造成内存占用,加载速度慢问题 由于在调用getInstance()之前就完成了初始化,如果需要给getInstance()函数传入参数,将会无法实现
public class Singleton { private static final Singleton instance = new Singleton(); private Singleton() { }; public static Singleton getInstance() { return instance; } }私有静态内部类 优点:延迟加载,线程安全
由于私有内部类不会在类的外部被使用,所以只有在调用getInstance()方法时才会被加载。同时依赖JVM的ClassLoader类加载机制保证了不会出现同步问题。 私有静态内部类保证了外部访问不到,内部类的静态属性,保证了当多线程调用getInstance时候,jvm加载Holder字节码文件时候对静态成员变量初始化保证线程安全。加载成功后instance实例已经创建,已经不存在线程安全的问题,同时保证了延迟加载(只要不调用getInstance方法就不会实例化Holder 类)。
public class Singleton { private Singleton() { }; public static Singleton getInstance() { return Holder.instance; } private static class Holder{ private static Singleton instance = new Singleton(); } }枚举方法
参见枚举类解析
线程安全由于枚举类的会在编译期编译为继承自java.lang.Enum的类,其构造函数为私有,不能再创建枚举对象,枚举对象的声明和初始化都是在static块中,所以由JVM的ClassLoader机制保证了线程的安全性。但是不能实现延迟加载
序列化由于枚举类型采用了特殊的序列化方法,从而保证了在一个JVM中只能有一个实例。
枚举类的实例都是static的,且存在于一个数组中,可以用values()方法获取该数组 在序列化时,只输出代表枚举类型的名字属性 name 反序列化时,根据名字在静态的数组中查找对应的枚举对象,由于没有创建新的对象,因而保证了一个JVM中只有一个对象
public enum Singleton { INSTANCE; public String error(){ return "error"; } }单例模式的破坏与防御
反射
对于枚举类,该破解方法不适用。
import java.lang.reflect.Constructor; public class TestCase { public void testBreak() throws Exception { Class<Singleton> clazz = (Class<Singleton>) Class.forName("Singleton"); Constructor<Singleton> constructor = clazz.getDeclaredConstructor(); constructor.setaccessible(true); Singleton instance1 = constructor.newInstance(); Singleton instance2 = constructor.newInstance(); System.out.println("singleton? " (instance1 == instance2)); } public static void main(String[] args) throws Exception{ new TestCase().testBreak(); } }序列化
对于枚举类,该破解方法不适用。
该测试首先需要声明Singleton为实现了可序列化接口
public class Singleton implements Serializable public class TestCase { private static final String SYSTEM_FILE = "save.txt"; public void testBreak() throws Exception { Singleton instance1 = Singleton.getInstance(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(SYSTEM_FILE)); oos.writeObject(instance1); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(SYSTEM_FILE)); Singleton instance2 = (Singleton) ois.readObject(); System.out.println("singleton? " (instance1 == instance2)); } public static void main(String[] args) throws Exception{ new TestCase().testBreak(); } }ClassLoader
JVM中存在两种ClassLoader,启动内装载器(bootstrap)和用户自定义装载器(user-defined class loader),在一个JVM中可能存在多个ClassLoader,每个ClassLoader拥有自己的NameSpace。一个ClassLoader只能拥有一个class对象类型的实例,但是不同的ClassLoader可能拥有相同的class对象实例,这时可能产生致命的问题。
防御
对于序列化与反序列化,我们需要添加一个自定义的反序列化方法,使其不再创建对象而是直接返回已有实例,就可以保证单例模式。
我们再次用下面的类进行测试,就发现结果为true。
public final class Singleton { private Singleton() { } private static final Singleton INSTANCE = new Singleton(); public static Singleton getInstance() { return INSTANCE; } private Object readResolve() throws ObjectStreamException { // instead of the object we re on, // return the class variable INSTANCE return INSTANCE; } public class TestCase { private static final String SYSTEM_FILE = "save.txt"; public void testBreak() throws Exception { Singleton instance1 = Singleton.getInstance(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(SYSTEM_FILE)); oos.writeObject(instance1); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(SYSTEM_FILE)); Singleton instance2 = (Singleton) ois.readObject(); System.out.println("singleton? " (instance1 == instance2)); } public static void main(String[] args) throws Exception { new TestCase().testBreak(); } } }单例模式性能总结
方式 | 优点 | 缺点 |
---|---|---|
饿汉式 | 线程安全, 调用效率高 | 不能延迟加载 |
懒汉式 | 线程安全, 可以延迟加载 | 调用效率不高 |
双重检测锁式 | 线程安全, 调用效率高, 可以延迟加载 | - |
静态内部类式 | 线程安全, 调用效率高, 可以延迟加载 | - |
枚举单例 | 线程安全, 调用效率高 | 不能延迟加载 |
单例性能测试
测试结果:
HungerSingleton 共耗时: 30 毫秒 LazySingleton 共耗时: 48 毫秒 DoubleCheckSingleton 共耗时: 25 毫秒 StaticInnerSingleton 共耗时: 16 毫秒 EnumSingleton 共耗时: 6 毫秒在不考虑延迟加载的情况下,枚举类型获得了**好的效率,懒汉模式由于每次方法都需要获取锁,所以效率**低,静态内部类与双重检查的效果类似。考虑到枚举可以 有效的避免序列化与反射,所以枚举是较好实现单例模式的方法。
public class TestCase { private static final String SYSTEM_FILE = "save.txt"; private static final int THREAD_COUNT = 10; private static final int CIRCLE_COUNT = 100000; public void testSingletonPerformance() throws IOException, InterruptedException { final CountDownLatch latch = new CountDownLatch(THREAD_COUNT); FileWriter writer = new FileWriter(new File(SYSTEM_FILE), true); long start = System.currentTimeMillis(); for (int i = 0; i < THREAD_COUNT; i) { new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < CIRCLE_COUNT; i) { Object instance = Singleton.getInstance(); } latch.countDown(); } }).start(); } latch.await(); long end = System.currentTimeMillis(); writer.append("Singleton 共耗时: " (end - start) " 毫秒\n"); writer.close(); } public static void main(String[] args) throws Exception{ new TestCase().testSingletonPerformance(); } }补充知识
类加载机制
static关键字的作用是把类的成员变成类相关,而不是实例相关,static块会在类首次被用到的时候进行加载,不是对象创建时,所以static块具有线程安全性
普通初始化块当Java创建一个对象时, 系统先为对象的所有实例变量分配内存(前提是该类已经被加载过了), 然后开始对这些实例变量进行初始化, 顺序是: 先执行初始化块或声明实例变量时指定的初始值(这两处执行的顺序与他们在源代码中排列顺序相同), 再执行构造器里指定的初始值.
静态初始化块
又名类初始化块(普通初始化块负责对象初始化, 类初始化块负责对类进行初始化). 静态初始化块是类相关的, 系统将在类初始化阶段静态初始化, 而不是在创建对象时才执行. 因此静态初始化块总是先于普通初始化块执行.
执行顺序
系统在类初始化以及对象初始化时, 不仅会执行本类的初始化块[static/non-static], 而且还会一直上溯到java.lang.Object类, 先执行Object类中的初始化块[static/non-static], 然后执行其父类的, **后是自己.
顶层类(初始化块, 构造器) -> … -> 父类(初始化块, 构造器) -> 本类(初始化块, 构造器)
小结
static{} 静态初始化块会在类加载过程中执行;
{} 则只是在对象初始化过程中执行, 但先于构造器;
内部类
内部类访问权限
Java 外部类只有两种访问权限:public/default, 而内部类则有四种访问权限:private/default/protected/public. 而且内部类还可以使用static修饰;内部类可以拥有private访问权限、protected访问权限、public访问权限及包访问权限。如果成员内部类Inner用private修饰,则只能在外部类的内部访问,如果用public修饰,则任何地方都能访问;如果用protected修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不一样,外部类只能被public和包访问两种权限修饰。成员内部类可以看做是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。 内部类分为成员内部类与局部内部类, 相对来说成员内部类用途更广泛, 局部内部类用的较少(匿名内部类除外), 成员内部类又分为静态(static)内部类与非静态内部类, 这两种成员内部类同样要遵守static与非static的约束(如static内部类不能访问外部类的非静态成员等) 非静态内部类
非静态内部类在外部类内使用时, 与平时使用的普通类没有太大区别; Java不允许在非static内部类中定义static成员,除非是static final的常量类型 如果外部类成员变量, 内部类成员变量与内部类中的方法里面的局部变量有重名, 则可**this, 外部类名.this加以区分. 非静态内部类的成员可以访问外部类的private成员, 但反之不成立, 内部类的成员不被外部类所感知. 如果外部类需要访问内部类中的private成员, 必须显示创建内部类实例, 而且内部类的private权限对外部类也是不起作用的: 静态内部类
使用static修饰内部类, 则该内部类隶属于该外部类本身, 而不属于外部类的某个对象. 由于static的作用, 静态内部类不能访问外部类的实例成员, 而反之不然; 匿名内部类
如果(方法)局部变量需要被匿名内部类访问, 那么该局部变量需要使用final修饰.
枚举
枚举类继承了java.lang.Enum, 而不是Object, 因此枚举不能显示继承其他类; 其中Enum实现了Serializable和Comparable接口(implements Comparable, Serializable); 非抽象的枚举类默认使用final修饰,因此枚举类不能派生子类; 枚举类的所有实例必须在枚举类的**行显示列出(枚举类不能**new来创建对象); 并且这些实例默认/且只能是public static final的; 枚举类的构造器默认/且只能是private; 枚举类通常应该设计成不可变类, 因此建议成员变量都用private final修饰; 枚举类不能使用abstract关键字将枚举类声明成抽象类(因为枚举类不允许有子类), 但如果枚举类里面有抽象方法, 或者枚举类实现了某个接口, 则定义每个枚举值时必须为抽象方法提供实现,
$(function () { $( pre.prettyprint code ).each(function () { var lines = $(this).text().split( \n ).length; var $numbering = $( ).addClass( pre-numbering ).hide(); $(this).addClass( has-numbering ).parent().append($numbering); for (i = 1; i <= lines; i ) { $numbering.append($( ).text(i)); }; $numbering.fadeIn(1700); }); });
相关推荐:
体验课预约试听
倒计时
课程热线:
客服在线时间:早上9点~下午6点,其他时间请在线预约报名或留言,谢谢!